Co robi functools.okłady?
W komentarzu do odpowiedzi na inne pytanie ktoś powiedział, że nie jest pewien, jakie functools.okłady robiły. Tak więc zadaję to pytanie, aby na StackOverflow był zapis tego w przyszłości: co to functools.okłady mają, dokładnie?
4 answers
Kiedy używasz dekoratora, zastępujesz jedną funkcję inną. Innymi słowy, jeśli masz dekoratora
def logged(func):
def with_logging(*args, **kwargs):
print func.__name__ + " was called"
return func(*args, **kwargs)
return with_logging
Then when you say
@logged
def f(x):
"""does some math"""
return x + x * x
To dokładnie to samo co powiedzenie
def f(x):
"""does some math"""
return x + x * x
f = logged(f)
I twoja funkcja f
zostanie zastąpiona funkcją with_logging. Niestety, oznacza to, że jeśli powiesz
print f.__name__
Wyświetli with_logging
ponieważ to nazwa twojej nowej funkcji. W rzeczywistości, jeśli spojrzysz na docstring dla f
, będzie pusty ponieważ with_logging
nie ma docstringu, a więc napisanego przez Ciebie docstringu już tam nie będzie. Ponadto, jeśli spojrzysz na wynik pydoc dla tej funkcji, nie będzie ona wyświetlana jako przyjmująca jeden argument x
; zamiast tego będzie wyświetlana jako przyjmująca *args
i **kwargs
, ponieważ to jest to, co zajmuje with_logging.
Jeśli użycie dekoratora zawsze oznaczało utratę informacji o funkcji, byłby to poważny problem. Dlatego mamy functools.wraps
. Przyjmuje funkcję używaną w dekoratorze i dodaje funkcjonalność kopiowania nad nazwą funkcji, docstringiem, listą argumentów, itd. A ponieważ wraps
jest dekoratorem, następujący kod robi poprawną rzecz:
from functools import wraps
def logged(func):
@wraps(func)
def with_logging(*args, **kwargs):
print func.__name__ + " was called"
return func(*args, **kwargs)
return with_logging
@logged
def f(x):
"""does some math"""
return x + x * x
print f.__name__ # prints 'f'
print f.__doc__ # prints 'does some math'
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-26 22:30:12
Bardzo często używam klas, zamiast funkcji, dla moich dekoratorów. Miałem pewne problemy z tym, ponieważ obiekt nie będzie miał wszystkich tych samych atrybutów, które są oczekiwane od funkcji. Na przykład obiekt nie będzie miał atrybutu __name__
. Miałem z tym konkretny problem, który był dość trudny do wyśledzenia, gdzie Django zgłaszał błąd "obiekt nie ma atrybutu' __name__
'". Niestety, dla dekoratorów klasowych, nie wierzę, że @wrap wykona tę robotę. Mam zamiast stworzył klasę base decorator Tak:
class DecBase(object):
func = None
def __init__(self, func):
self.__func = func
def __getattribute__(self, name):
if name == "func":
return super(DecBase, self).__getattribute__(name)
return self.func.__getattribute__(name)
def __setattr__(self, name, value):
if name == "func":
return super(DecBase, self).__setattr__(name, value)
return self.func.__setattr__(name, value)
Ta klasa proxy wszystkie wywołania atrybutu do funkcji, która jest dekorowana. Możesz więc teraz utworzyć prosty dekorator, który sprawdza, czy podane są dwa argumenty w ten sposób:
class process_login(DecBase):
def __call__(self, *args):
if len(args) != 2:
raise Exception("You can only specify two arguments")
return self.func(*args)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-29 20:42:45
Wymagane umiejętności: musisz wiedzieć, jak używać dekoratorów i specjalnie z okładami. Ten komentarz wyjaśnia to nieco jasno lub ten link również wyjaśnia to całkiem dobrze.
-
Gdy używamy np: @wraps, po którym następuje nasza własna funkcja wrapper. Zgodnie ze szczegółami podanymi w tym linku, jest napisane, że
Functools.wraps jest wygodną funkcją do wywoływania update_wrapper () jako dekoratora funkcji, podczas definiowania wrappera funkcja.
Jest to równoważne częściowemu (update_wrapper, wrapped=wrapped, assigned=assigned, updated=updated).
Więc @ wraps decorator faktycznie daje połączenie do functools.partial(func [, * args] [, * * keywords]).
Functools.definicja partial () mówi, żeFunkcja partial() jest używana do częściowej aplikacji funkcji, która "zawiesza" część argumentów funkcji i / lub słów kluczowych, co powoduje utworzenie nowego obiektu z uproszczonym podpisem. Na przykład, partial () może być użyty do utworzenia funkcji wywołującej, która zachowuje się jak funkcja int (), gdzie argument bazowy domyślnie wynosi dwa:
>>> from functools import partial
>>> basetwo = partial(int, base=2)
>>> basetwo.__doc__ = 'Convert base 2 string to an int.'
>>> basetwo('10010')
18
Co prowadzi mnie do wniosku, że @wraps wywołuje partial () i przekazuje funkcję wrapper jako parametr do niej. Partial() na końcu zwraca uproszczoną wersję, tzn. obiekt tego, co znajduje się wewnątrz funkcji wrapper, a nie samą funkcję wrapper.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-10 03:14:01
W skrócie, functools.wraps jest tylko funkcją regularną. Rozważmy Ten oficjalny przykład . Za pomocą kodu źródłowego możemy zobaczyć więcej szczegółów na temat implementacji i kroków działania w następujący sposób:
- wraps (f) zwraca obiekt, powiedzmy O1 . Jest to obiekt class Partial
- następnym krokiem jest @o1... który jest notacją dekoratorską w Pythonie. Oznacza
Wrapper=O1.__ call _ _ (wrapper)
Sprawdzanie realizacji __call__, widzimy, że po tym kroku (lewa strona) wrapper staje się obiektem powstałym przez jaźń.func (*self.args, * args, * * newkeywords) sprawdzanie utworzenia O1 w _ _ new _ _ , znamy siebie.func jest funkcją update_wrapper . Używa parametru *args , prawej strony wrapper, jako jego 1st parametr. Sprawdzając ostatni krok update_wrapper , widać, że zwracany jest po prawej stronie wrapper, z niektórymi atrybutami modyfikowanymi w razie potrzeby.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-30 04:27:08