Что functools.обертывания делать?
в комментарии к ответ на другой вопрос, кто-то сказал, что они не были уверены, что functools.обертывания делали. Поэтому я задаю этот вопрос, чтобы была запись об этом на StackOverflow для дальнейшего использования: что делает functools.обертывания делать?
4 ответа:
когда вы используете декоратор, вы заменяете одну функцию на другую. Другими словами, если у вас есть декоратор
def logged(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
это точно так же, как говорят
def f(x): """does some math""" return x + x * x f = logged(f)
и функция
f
заменяется функцией with_logging. К сожалению, это означает, что если вы говоритеprint f.__name__
он будет печатать
with_logging
потому что это имя новой функции. На самом деле, если вы посмотрите в строке документа дляf
, оно будет пустым, потому чтоwith_logging
не имеет docstring, и поэтому docstring, который вы написали, больше не будет там. Кроме того, если вы посмотрите на результат pydoc для этой функции, он не будет указан как принимающую один аргументx
; вместо этого он будет указан как принимать*args
и**kwargs
потому что это то, что with_logging принимает.если бы использование декоратора всегда означало потерю этой информации о функции, это было бы серьезной проблемой. Вот почему мы
functools.wraps
. Это берет функцию, используемую в декораторе, и добавляет функциональность копирования по имени функции, docstring, списку аргументов и т. д. И с тех порwraps
сам декоратор, следующий код делает правильную вещь: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'
Я очень часто использую классы, а не функции, для моих декораторов. У меня возникли некоторые проблемы с этим, потому что объект не будет иметь все те же атрибуты, которые ожидаются от функции. Например, объект не будет иметь атрибут
__name__
. У меня была конкретная проблема с этим, что было довольно трудно проследить, где Django сообщал об ошибке "объект не имеет атрибута'__name__
'". К сожалению, для декораторов класса я не верю, что @wrap выполнит эту работу. У меня есть вместо этого создал базовый класс декоратора вот так: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)
этот класс проксирует все вызовы атрибута к функции, которая оформляется. Итак, теперь вы можете создать простой декоратор, который проверяет, что 2 аргумента указаны следующим образом:
class process_login(DecBase): def __call__(self, *args): if len(args) != 2: raise Exception("You can only specify two arguments") return self.func(*args)
обязательное условие: Вы должны знать, как использовать декораторы и особенно с обертываниями. Это комментарий объясняет это немного ясно или это ссылке также объясняет это довольно хорошо.
всякий раз, когда мы используем, например: @wraps, за которым следует наша собственная функция обертки. В соответствии с деталями, приведенными в этом ссылке говорится , что
functools.wraps-это удобная функция для вызова update_wrapper () как декоратор функции, при определении функции-оболочки.
Это эквивалентно частичной(update_wrapper, обернутый=укутав, присвоенный=назначенных обновленными=обновлено).
Итак, @ wraps decorator фактически дает вызов functools.частичный(func [, * args] [, * * keywords]).
В functools.частичное () определение говорит, что
частичный() используется для применения частичной функции, которая "замораживает" некоторую часть a Аргументы и/или ключевые слова функции, приводящие к новому объекту с упрощенной сигнатурой. Например, partial () можно использовать для создания вызываемого объекта, который ведет себя как функция int (), где базовый аргумент по умолчанию равен двум:
>>> from functools import partial >>> basetwo = partial(int, base=2) >>> basetwo.__doc__ = 'Convert base 2 string to an int.' >>> basetwo('10010') 18
что приводит меня к выводу, что @wraps дает вызов partial() и передает ему функцию-оболочку в качестве параметра. Partial() в конце возвращает упрощенную версию, т. е. объект того, что находится внутри оболочки функция, а не сама функция-оболочка.
короче, functools.обертывания - это просто обычная функция. Давайте рассмотрим это официальный пример. С помощью исходный код, мы можем увидеть более подробную информацию о реализации и беговых шагов следующим образом:
- wraps (f) возвращает объект, скажем O1. Это объект частичная класс
- следующий шаг: @O1... что обозначение декоратор в Python. Это значит
обертка=O1.__ call__(обертка)
проверка реализации _ _ call__, мы видим, что после этого шага (с левой стороны )фантик становится объектом в результате self.func (*self.args, * args, * * newkeywords) проверка создания O1 на _ _ new__, мы знаем self.func функция update_wrapper. Он использует параметр * args, справа фантик, как его 1-й параметр. Проверка последнего шага update_wrapper, можно увидеть правую сторону фантик возвращается, с некоторыми атрибутами изменять по мере необходимости.