Python 3.5 Декораторы и функциональные поля - PullRequest
0 голосов
/ 15 февраля 2019

У меня есть следующий фрагмент кода:

def wrapper(func):
    def wrapped(*args, **kwargs):
        func.var = 0
        return func(*args, **kwargs)
    return wrapped

@wrapper
def f_out():
    print(f_out.var)

Не могли бы вы объяснить мне, почему бег f_out() повышает:

AttributeError: 'function' object has no attribute 'var'

РЕДАКТИРОВАТЬ

Мне пришлось уточнить, так как ответ дал мне альтернативу, но это не сработает для ситуации, которую я хочу.Учитывая следующий фрагмент:

def wrapper(func):
    def wrapped(*args, **kwargs):
        func.var = 0
        ret = func(*args, **kwargs)
        print(func.var)
    return wrapped

@wrapper
def f_out():
    f_out.var = 1
f_out()
print(f_out.var)

Я получаю в качестве вывода:

0
1

Почему это происходит?

Ответы [ 3 ]

0 голосов
/ 15 февраля 2019

Правильный способ - вернуть упакованную функцию и изменить ее перед возвратом:

def wrapper(func):
    def wrapped(*args, **kwargs):
        return func(*args, **kwargs)
    wrapped.var = 0
    return wrapped

@wrapper
def f_out():
    print(f_out.var)

Вы правильно получаете:

print(f_out())

дает

0

Обновленный фрагмент дважды изменяет атрибут var:

  • первый раз в оболочке, где он устанавливает атрибут для исходной функции в 0 и печатает его послевызывая исходную функцию
  • , затем, когда исходная функция вызывается из оболочки, она устанавливает атрибут для функции, на которую ссылаются как f_out, равной 1. Но в этот момент функция, на которую ссылаетсяf_out - это упакованная функция, которая больше не является исходной.

Поэтому при последующем выводе f_out.var вы печатаете атрибут для упакованной функции, равный 1.

Вот немного измененный код, демонстрирующий это:

def wrapper(func):
    def wrapped(*args, **kwargs):
        wrapped.orig = func          # keeps a ref to the original function
        func.var = 0
        ret = func(*args, **kwargs)
        print(func.var)
    return wrapped

@wrapper
def f_out():
    f_out.var = 1

f_out()
print(f_out.var, f_out.orig.var)

Он печатает

0
1 0
0 голосов
/ 15 февраля 2019

Декоратор оборачивает вашу функцию при вызове.Поэтому, когда вы звоните f_out(), он возвращает 1.Это вызывающая функция, а не ее определение.

@wrapper
def f_out()

равно wrapper(f_out) при вызове.

Когда вы пытаетесь напечатать f_out.var, оно возвращает значение из функцииопределение.

0 голосов
/ 15 февраля 2019

вы должны добавить return wrapped вместо этого, если return func попробуйте это:

def wrapper(func):
    def wrapped(*args, **kwargs):
        func.var = 0
        return func(*args, **kwargs)
    return wrapped

@wrapper
def f_out():
    print(f_out.var)
...