Mam problem z przeniesieniem zmiennej „Insurance_mode” przez dekoratora. Zrobiłbym to przez następujące oświadczenie dekoratora:
@execute_complete_reservation(True)
def test_booking_gta_object(self):
self.test_select_gta_object()
ale niestety to stwierdzenie nie działa. Być może istnieje lepszy sposób na rozwiązanie tego problemu.
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
execute_complete_reservation
bierze dwa parametry, ale przekazujesz jeden. Dekoratory to po prostu cukier składniowy do zawijania funkcji wewnątrz innych funkcji. Zobacz docs.python.org/reference/compound_stmts.html#function dla kompletnej dokumentacji.Odpowiedzi:
Składnia dekoratorów z argumentami jest nieco inna - dekorator z argumentami powinien zwrócić funkcję, która przyjmuje funkcję i zwraca inną funkcję. Więc naprawdę powinien zwrócić normalnego dekoratora. Trochę mylące, prawda? Chodzi mi o to że:
Tutaj możesz przeczytać więcej na ten temat - możliwe jest również zaimplementowanie tego przy użyciu obiektów na żądanie i to również tam wyjaśniono.
źródło
return function(*args, **kwargs)
@decorator()
nie tylko@decorator
, nawet jeśli masz tylko opcjonalne argumenty.Edycja : aby uzyskać dogłębne zrozumienie mentalnego modelu dekoratorów, spójrz na tę niesamowitą rozmowę Pycon. warte 30 minut.
Jednym ze sposobów myślenia o dekoratorach z argumentami jest
przetłumaczyć na
Więc jeśli dekorator miał argumenty,
przetłumaczyć na
decorator_with_args
to funkcja, która akceptuje niestandardowy argument i zwraca rzeczywisty dekorator (który zostanie zastosowany do dekorowanej funkcji).Używam prostej sztuczki z częściami, aby ułatwić dekoratorom
Aktualizacja:
Powyżej
foo
staje sięreal_decorator(foo)
Jednym z efektów dekorowania funkcji
foo
jest zastąpienie nazwy w deklaracji dekoratora.foo
jest „zastępowane” przez to, co jest zwracanereal_decorator
. W takim przypadku nowy obiekt funkcji.Wszystkie
foo
metadane są nadpisane, w szczególności nazwa dokumentu i nazwa funkcji.funkools.wraps zapewnia nam wygodną metodę „podniesienia” dokumentu i nazwy do zwracanej funkcji.
źródło
@functools.wraps
?functool.wraps
. Dodanie go w tym przykładzie może jeszcze bardziej dezorientować czytelników.arg
tu jest !?bar
argumentowireal_decorator
?Chciałbym pokazać pomysł, który jest IMHO dość elegancki. Rozwiązanie zaproponowane przez t.dubrownik pokazuje wzór, który jest zawsze taki sam: potrzebujesz trójwarstwowego opakowania niezależnie od tego, co robi dekorator.
Pomyślałem więc, że jest to praca dla meta-dekoratora, czyli dekoratora dla dekoratorów. Ponieważ dekorator jest funkcją, faktycznie działa jako zwykły dekorator z argumentami:
Można to zastosować do zwykłego dekoratora w celu dodania parametrów. Powiedzmy na przykład, że mamy dekorator, który podwaja wynik funkcji:
Z
@parametrized
możemy zbudować ogólny@multiply
dekorator posiadający parametrKonwencjonalnie pierwszy parametr sparametryzowany dekoratora jest funkcja, natomiast pozostałe argumenty będą odpowiadały parametrowi sparametryzowanego dekoratora.
Ciekawym przykładem użycia może być asertywna dekoratorka bezpieczna dla typu:
Ostatnia uwaga: tutaj nie używam
functools.wraps
funkcji otoki, ale zalecałbym używanie jej przez cały czas.źródło
@wraps
do mnie w mojej konkretnej sprawie.@parametrized
sztuczki. Problem, jaki miałem, polegał na tym, że zapomniałem, że@
składnia jest równa rzeczywistym wywołaniom (jakoś to wiedziałem i nie wiedziałem o tym w tym samym czasie, co można zebrać na podstawie mojego pytania). Więc jeśli chcesz przetłumaczyć@
składnię na przyziemne wywołania, by sprawdzić, jak to działa, lepiej najpierw skomentuj to tymczasowo, albo skończysz na dwukrotnym wywołaniu i uzyskaniu wynikówOto nieco zmodyfikowana wersja odpowiedzi t.dubrownik . Dlaczego?
Więc użyj
@functools.wraps()
:źródło
Zakładam, że twoim problemem jest przekazywanie argumentów dekoratorowi. Jest to trochę trudne i nie jest proste.
Oto przykład, jak to zrobić:
Wydruki:
Aby uzyskać więcej informacji, zobacz artykuł Bruce'a Eckela.
źródło
__name__
czego nie będzie miało wystąpienie klasy dekoratora?class Foo: @MyDec(...) def method(self, ...): blah
nie działa, ponieważFoo().method
nie będzie metodą powiązaną i nie przejdzieself
automatycznie. To również można naprawić, tworzącMyDec
deskryptor i tworząc powiązane metody__get__
, ale jest to bardziej zaangażowane i znacznie mniej oczywiste. W końcu klasy dekoratorów nie są tak wygodne, jak się wydaje.Zastosowanie dekoratora
A później
produkuje
ale
produkuje
źródło
To jest szablon dekoratora funkcji, który nie wymaga,
()
jeśli nie zostaną podane żadne parametry:przykład tego podano poniżej:
źródło
factor_or_func
(lub jakikolwiek inny parametr) nigdy nie powinien zostaje przeniesiony wwrapper()
.locals()
?()
.W moim przypadku postanowiłem rozwiązać ten problem za pomocą jednowierszowej lambdy, aby utworzyć nową funkcję dekoratora:
Po uruchomieniu drukuje:
Być może nie tak rozszerzalne jak inne rozwiązania, ale działało dla mnie.
źródło
Napisanie dekoratora, który działa z parametrem i bez niego, stanowi wyzwanie, ponieważ w tych dwóch przypadkach Python oczekuje zupełnie innego zachowania! Wiele odpowiedzi próbowało obejść ten problem, a poniżej jest poprawa odpowiedzi przez @ norok2. W szczególności ta odmiana eliminuje użycie
locals()
.Postępując zgodnie z przykładem podanym przez @ norok2:
Graj z tym kodem .
Chodzi o to, że użytkownik musi podać klucz, pary wartości parametrów zamiast parametrów pozycyjnych, a pierwszy parametr jest zarezerwowany.
źródło
Powszechnie wiadomo, że następujące dwa fragmenty kodu są prawie równoważne:
Częstym błędem jest myślenie, że
@
po prostu kryje się argument po lewej stronie.Byłoby o wiele łatwiej pisać dekoratorów, jeśli powyższe jest, jak
@
działa. Niestety, nie tak to się robi.Zastanów się nad dekoratorem,
Wait
który na kilka sekund wstrzymuje wykonywanie programu. Jeśli nie upłynie czas oczekiwania, wartość domyślna to 1 sekunda. Przypadki użycia pokazano poniżej.Gdy
Wait
ma argument, taki jak@Wait(3)
, wówczas wywołanieWait(3)
jest wykonywane wcześniej cokolwiek innego się wydarzy.Oznacza to, że następujące dwa fragmenty kodu są równoważne
To jest problem.
Jedno rozwiązanie pokazano poniżej:
Zacznijmy od utworzenia następującej klasy
DelayedDecorator
:Teraz możemy pisać takie rzeczy jak:
Uwaga:
dec
nie akceptuje wielu argumentów.dec
akceptuje tylko funkcję do zapakowania.import inspect class PolyArgDecoratorMeta (type): def call (Wait, * args, ** kwargs): try: arg_count = len (args) if (arg_count == 1): if callable (args [0]): SuperClass = inspect. getmro (PolyArgDecoratorMeta) [1] r = SuperClass. połączenie (czekaj, args [0]) else: r = DelayedDecorator (czekaj, * args, ** kwargs) else: r = DelayedDecorator (czekaj, * args, ** kwargs) na koniec: pass return r
klasa czasu importu Wait (metaclass = PolyArgDecoratorMeta): def init (i, func, delay = 2): i._func = func i._delay = opóźnienie
Poniższe dwa fragmenty kodu są równoważne:
Możemy drukować
"something"
na konsoli bardzo powoli, w następujący sposób:Uwagi końcowe
To może wyglądać jak dużo kodu, ale nie trzeba pisać klas
DelayedDecorator
iPolyArgDecoratorMeta
za każdym wymiarze. Jedyny kod, który musisz osobiście napisać w następujący sposób, który jest dość krótki:źródło
zdefiniuj tę „funkcję dekoratora”, aby wygenerować niestandardową funkcję dekoratora:
użyj tego w ten sposób:
źródło
Świetne odpowiedzi powyżej. Ten ilustruje również
@wraps
, który pobiera ciąg dokumentu i nazwę funkcji z oryginalnej funkcji i stosuje ją do nowej zapakowanej wersji:Wydruki:
źródło
W przypadku, gdy zarówno funkcja, jak i dekorator muszą wziąć argumenty, możesz zastosować poniższe podejście.
Na przykład jest dekorator o nazwie,
decorator1
który bierze argumentTeraz, jeśli
decorator1
argument musi być dynamiczny lub przekazywany podczas wywoływania funkcji,W powyższym kodzie
seconds
jest argumentem zadecorator1
a, b
są argumentamifunc1
źródło