2016-12-01 2 views
2

Так у меня есть этот код:Функция Атрибуты - Область

def collect_input(func): 
    """ 
    A decorator which adds an all_input attribute to the wrapped function. 
    This attribute collects any input passed to the function. 
    """ 
    def wrapper(*args, **kwargs): 
     wrapper.all_input.append(*args) 
     return func(*args, **kwargs) 

    wrapper.all_input = [] 
    return wrapper 

@collect_input 
def foo(bar): 
    print('in foo') 

foo(5) 
foo('spam') 

print(foo.all_input) 

Мой вопрос: Почему вы можете получить доступ к foo.all_input, если он объявлен в collect_input объеме?

+0

На самом деле, вы не доступ к '' foo.all_input' но wrapper.all_input' потому, что вы завернуты вашей функция с декораторой который возвращает функцию 'wrapper'. – Kasramvd

ответ

2

Приятная вещь Python заключается в том, что он имеет набор правил для передачи своих объектов и очень мало исключений из этих правил.

В этом случае функция, которая украшена, является обычным объектом Python. Тот, который также может быть вызван.

Что происходит в линии wrapper.all_input = [] внутри collect_input является то, что он устанавливает атрибут на объекте - в этой точке под названием wrapper - но это объект, который будет возвращен и занять место функции foo в глобальном масштабе. Вот как работают декораторы.

Так давайте есть корыто, шаг за шагом, чтобы сделать его как можно более ясно:

  1. При выполнении кода выше, определяет collect_input функцию - которая предназначена для использования в качестве декоратора.

  2. Затем он определяет функцию foo, но перед ее добавлением в глобальную область она передается в функцию collect_input. Вот что делает синтаксис «@». До его существования способ украсить функции, чтобы сначала определить функцию, а затем заменить ее для возвращаемого значения декоратора с нормальным назначением. Таким образом, приведенный выше код такой же, как:

    четкости Foo (...): ...

    обув = collect_input (Foo)

  3. Внутри "collect_input", оригинальный foo FUNC будет вызываться внутри новой функции wrapper. Эта функция wrapper: новый (функциональный) объект, созданный каждый раз, когда вызывается декоратор collect_input, является объектом, который заменит самое внешнее определение foo. Вы можете видеть, что внутри кода для wrapper есть дополнительный код для того, чтобы сделать то, что означает collect_input: аннотировать входные параметры в списке, прикрепленный к нему, а затем возобновить вызов исходной функции - foo в этом случае.

  4. Наконец wrapper объект, который возвращается collect_input занимает место foo, но имеет all_inputs список прилагается к нему внутри вызова декоратора. Таким образом, он может быть доступен в глобальной области как атрибут объекта foo - независимо от того, где он был определен. Обратите внимание, что вы не можете, вне функции, использовать имена func или wrapper, как и ожидалось.

1
@collect_input 
def foo(bar): 
    print(foo.__name__) # wrapper 
    print('in foo') 

вы можете увидеть, как wrapper занимает места foo

Смежные вопросы