722 Stimmen

Dekorateure mit Parametern?

Ich habe ein Problem mit der Übertragung der Variablen insurance_mode durch den Decorator. Ich würde es mit der folgenden Decorator-Anweisung tun:

@execute_complete_reservation(True)
def test_booking_gta_object(self):
    self.test_select_gta_object()

aber leider funktioniert diese Anweisung nicht. Vielleicht gibt es eine bessere Möglichkeit, dieses Problem zu lösen.

def execute_complete_reservation(test_case,insurance_mode):
    def inner_function(self,*args,**kwargs):
        self.test_create_qsf_query()
        test_case(self,*args,**kwargs)
        self.test_select_room_option()
        if insurance_mode:
            self.test_accept_insurance_crosseling()
        else:
            self.test_decline_insurance_crosseling()
        self.test_configure_pax_details()
        self.test_configure_payer_details

    return inner_function

5 Stimmen

Dein Beispiel ist nicht syntaktisch korrekt. execute_complete_reservation erwartet zwei Parameter, aber du übergibst ihm nur einen. Dekorateure sind nur syntaktischer Zucker, um Funktionen in andere Funktionen einzuschließen. Siehe docs.python.org/reference/compound_stmts.html#function für die vollständige Dokumentation.

44voto

Ross Rogers Punkte 22353

Ich nehme an, Ihr Problem besteht darin, Argumente an Ihren Dekorateur zu übergeben. Dies ist etwas knifflig und nicht einfach.

Hier ist ein Beispiel, wie das gemacht wird:

class MyDec(object):
    def __init__(self,flag):
        self.flag = flag
    def __call__(self, original_func):
        decorator_self = self
        def wrappee( *args, **kwargs):
            print 'in decorator before wrapee with flag ',decorator_self.flag
            original_func(*args,**kwargs)
            print 'in decorator after wrapee with flag ',decorator_self.flag
        return wrappee

@MyDec('foo de fa fa')
def bar(a,b,c):
    print 'in bar',a,b,c

bar('x','y','z')

Druckt:

in decorator before wrapee with flag  foo de fa fa
in bar x y z
in decorator after wrapee with flag  foo de fa fa

Siehe den Artikel von Bruce Eckel für weitere Details.

23 Stimmen

Vorsicht vor Dekoriererklassen. Sie funktionieren nicht bei Methoden, es sei denn, Sie erfinden manuell die Logik von Instanzmethodendeskriptoren neu.

9 Stimmen

Delnan, möchtest du das näher erläutern? Ich musste dieses Muster nur einmal verwenden, also bin ich bisher noch nicht auf etwaige Fallstricke gestoßen.

2 Stimmen

@RossRogers Meine Vermutung ist, dass @delnan sich auf Dinge wie __name__ bezieht, die eine Instanz der Decorator-Klasse nicht haben wird?

25voto

Gajendra D Ambi Punkte 3270
def dekorator(argument):
    def echter_dekorator(funktion):
        def hülse(*args):
            for arg in args:
                assert type(arg)==int,f'{arg} ist keine Ganzzahl'
            ergebnis = funktion(*args)
            ergebnis = ergebnis*argument
            return ergebnis
        return hülse
    return echter_dekorator

Verwendung des Dekorators

@dekorator(2)
def addierer(*args):
    summe=0
    for i in args:
        summe+=i
    return summe

Dann

addierer(2,3)

ergibt

10

aber

addierer('hi',3)

ergibt

---------------------------------------------------------------------------
AssertionError                            Traceback (most recent call last)
 in 
----> 1 addierer('hi',3)

 in hülse(*args)
      3        def hülse(*args):
      4            for arg in args:
----> 5                assert type(arg)==int,f'{arg} ist keine Ganzzahl'
      6            ergebnis = funktion(*args)
      7            ergebnis = ergebnis*argument

AssertionError: hi ist keine Ganzzahl

5 Stimmen

Von allen Beiträgen hier, erwies sich diese Antwort als die nützlichste für mein Verständnis, wie das Argument übergeben und behandelt wird.

24voto

norok2 Punkte 21112

Dies ist eine Vorlage für einen Funktion-Decorator, der nicht erfordert, () wenn keine Parameter übergeben werden sollen und sowohl positionale als auch Schlüsselwortparameter unterstützt (erfordert jedoch die Überprüfung auf locals() ob der erste Parameter die zu dekorierende Funktion ist oder nicht):

import functools

def decorator(x_or_func=None, *decorator_args, **decorator_kws):
    def _decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kws):
            if 'x_or_func' not in locals() \
                    or callable(x_or_func) \
                    or x_or_func is None:
                x = ...  # <-- Standardwert für `x`
            else:
                x = x_or_func
            return func(*args, **kws)

        return wrapper

    return _decorator(x_or_func) if callable(x_or_func) else _decorator

Ein Beispiel dafür wird unten gegeben:

def multiplying(factor_or_func=None):
    def _decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            if 'factor_or_func' not in locals() \
                    or callable(factor_or_func) \
                    or factor_or_func is None:
                factor = 1
            else:
                factor = factor_or_func
            return factor * func(*args, **kwargs)
        return wrapper
    return _decorator(factor_or_func) if callable(factor_or_func) else _decorator

@multiplying
def summing(x): return sum(x)

print(summing(range(10)))
# 45

@multiplying()
def summing(x): return sum(x)

print(summing(range(10)))
# 45

@multiplying(10)
def summing(x): return sum(x)

print(summing(range(10)))
# 450

Alternativ, wenn keine positionalen Argumente benötigt werden, kann man den Bedarf an der Überprüfung des ersten Parameters innerhalb des wrapper() entspannen (und somit die Verwendung von locals() entfallen lassen):

import functools

def decorator(func_=None, **decorator_kws):
    def _decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kws):
            return func(*args, **kws)
        return wrapper

    if callable(func_):
        return _decorator(func_)
    elif func_ is None:
        return _decorator
    else:
        raise RuntimeWarning("Positionale Argumente werden nicht unterstützt.")

Ein Beispiel dafür wird unten gegeben:

import functools

def multiplying(func_=None, factor=1):
    def _decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            return factor * func(*args, **kwargs)
        return wrapper

    if callable(func_):
        return _decorator(func_)
    elif func_ is None:
        return _decorator
    else:
        raise RuntimeWarning("Positionale Argumente werden nicht unterstützt.")

@multiplying
def summing(x): return sum(x)

print(summing(range(10)))
# 45

@multiplying()
def summing(x): return sum(x)

print(summing(range(10)))
# 45

@multiplying(factor=10)
def summing(x): return sum(x)

print(summing(range(10)))
# 450

@multiplying(10)
def summing(x): return sum(x)
print(summing(range(10)))
# RuntimeWarning Traceback (most recent call last)
#    ....
# RuntimeWarning: Positionale Argumente werden nicht unterstützt.

(teilweise überarbeitet von @ShitalShah's Antwort)

0 Stimmen

Beachten Sie auch, dass factor_or_func (oder ein anderer Parameter) niemals neu zugewiesen werden sollte in wrapper().

2 Stimmen

Warum musst du locals() überprüfen?

0 Stimmen

@ShitalShah das betrifft den Fall, in dem der Decorator ohne () verwendet wird.

18voto

Henshal B Punkte 794

Einfach wie das

def real_decorator(any_number_of_arguments):
   def pseudo_decorator(function_to_be_decorated):

       def real_wrapper(function_arguments):
           print(function_arguments)
           result = function_to_be_decorated(any_number_of_arguments)
           return result

       return real_wrapper
   return pseudo_decorator

Jetzt

@real_decorator(any_number_of_arguments)
def some_function(function_arguments):
        return "Any"

3 Stimmen

Beachten Sie, dass dies nicht ähnlich wie der normale Decorator funktioniert, wenn das any_number_of_arguments ein optionales Argument ist, müssen Sie dennoch () am Ende des Decorators schreiben.

0 Stimmen

Die Frage bezieht sich speziell auf Dekorateure mit Parametern und betrifft keine optionalen Parameter.

10voto

Bildbeschreibung hier eingeben

  • Hier haben wir display info zweimal mit zwei verschiedenen Namen und zwei verschiedenen Alter ausgeführt.
  • Jetzt fügten unsere Dekoratoren jedes Mal, wenn wir display info ausführten, auch die Funktionalität hinzu, eine Zeile vor und eine Zeile nach dieser umschlossenen Funktion auszugeben.

    def decorator_function(original_function): def wrapper_function(*args, *kwargs): print('Vor der Ausführung', original_function.name) result = original_function(args, **kwargs) print('Nach der Ausführung', original_function.name, '\n') return result return wrapper_function

    @decorator_function def display_info(name, age): print('display_info wurde mit Argumenten ({}, {}) ausgeführt'.format(name, age))

    display_info('Mr Bean', 66) display_info('MC Jordan', 57)

Ausgabe:

Vor der Ausführung display_info
display_info wurde mit Argumenten (Mr Bean, 66) ausgeführt
Nach der Ausführung display_info 

Vor der Ausführung display_info
display_info wurde mit Argumenten (MC Jordan, 57) ausgeführt
Nach der Ausführung display_info 
  • Lassen Sie uns nun fortfahren und unsere Dekoratorfunktion annehmen.

  • Zum Beispiel, wenn ich einen anpassbaren Präfix für all diese Druckanweisungen innerhalb des Wrappers haben möchte.

  • Dies wäre jetzt ein guter Kandidat für ein Argument des Dekorators.

  • Das Argument, das wir übergeben werden, ist dieses Präfix. Um dies zu tun, fügen wir einfach eine weitere äußere Schicht zu unserem Dekorator hinzu, also nenne ich diese Funktion einen Präfix-Dekorator.

    def prefix_decorator(prefix): def decorator_function(original_function): def wrapper_function(*args, *kwargs): print(prefix, 'Vor der Ausführung', original_function.name) result = original_function(args, **kwargs) print(prefix, 'Nach der Ausführung', original_function.name, '\n') return result return wrapper_function return decorator_function

    @prefix_decorator('LOG:') def display_info(name, age): print('display_info wurde mit Argumenten ({}, {}) ausgeführt'.format(name, age))

    display_info('Mr Bean', 66) display_info('MC Jordan', 57)

Ausgabe:

LOG: Vor der Ausführung display_info
display_info wurde mit Argumenten (Mr Bean, 66) ausgeführt
LOG: Nach der Ausführung display_info 

LOG: Vor der Ausführung display_info
display_info wurde mit Argumenten (MC Jordan, 57) ausgeführt
LOG: Nach der Ausführung display_info 
  • Jetzt haben wir dieses LOG: Präfix vor unseren Druckanweisungen in unserer Wrapper-Funktion und Sie können dies jederzeit ändern.

0 Stimmen

Das GIF mit dem Code ist cool anzusehen, aber schwer zu lesen. Ich liebe es, wenn der Text, den ich zu lesen versuche, nicht vor meinen Augen verschwindet :D

0 Stimmen

Jeder hat das Recht zu schauen, jemand lernt von dem, was er sieht und verwendet es, weil er den Zweck versteht, und jemand kann nicht den Sinn von allem sehen und sucht nach Kritik, also geht es alles um den Blick. Zum Beispiel verstehe ich deinen Kommentar nicht, d.h. seinen Sinn. :D

0 Stimmen

Mein Punkt ist, dass der Code im animierten GIF schwer zu lesen ist, weil er beim Lesen immer wieder verschwindet, daher ist es ziemlich schwierig, "aus dem Gesehenen zu lernen". Zum Beispiel bleibt die letzte Zeile weniger als 0,3 Sekunden auf dem Bildschirm. Jemand könnte den Sinn hinter allem nicht erkennen und nach Kritik suchen. Ich sehe keinen Sinn darin, ein animiertes GIF des Codes Zeile für Zeile zu erstellen, anstatt ihn einfach als Code-Block in die Antwort zu kopieren. Ich suche nicht "nur nach Kritik". Ich habe die Antwort gelesen und Kommentare verwendet, um Verbesserungen vorzuschlagen, was buchstäblich ihr Zweck ist.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X