Python 3 - поток выполнения декораторов - PullRequest
0 голосов
/ 05 сентября 2018

Приведенный ниже пример взят из 3-й редакции книги по питону, раздел 9.5. Я поместил точки останова в каждой строке, чтобы понять ход выполнения. Ниже приведен пример кода, его вывод и вопросы, которые у меня есть. Я попытался объяснить свой вопрос, дайте мне знать, если вам нужна дополнительная информация.

from functools import wraps, partial
import logging

# Utility decorator to attach a function as an attribute of obj
def attach_wrapper(obj, func=None):
    if func is None:
        return partial(attach_wrapper, obj)
    setattr(obj, func.__name__, func)
    return func

def logged(level, name=None, message=None):


    def decorate(func):
        logname = name if name else func.__module__
        log = logging.getLogger(logname)
        logmsg = message if message else func.__name__

        @wraps(func)
        def wrapper(*args, **kwargs):
            log.log(level, logmsg)
            return func(*args, **kwargs)


        @attach_wrapper(wrapper)
        def set_message(newmsg):
            nonlocal logmsg
            logmsg = newmsg


        return wrapper
    return decorate

# Example use
@logged(logging.DEBUG)
def add(x, y):
    return x + y


logging.basicConfig(level=logging.DEBUG)
add.set_message('Add called')
#add.set_level(logging.WARNING)
print (add(2, 3))

вывод

DEBUG:__main__:Add called
5

Я понимаю концепцию декораторов, но это немного сбивает с толку.

сценарий 1 . Когда следующая строка отлажена @ logged (logging.DEBUG) , мы получаем decorate = .decorate в 0x000000000 <адрес памяти >>

Вопрос : почему управление возвращается к выполнению функции def decorate? Это потому, что функция decorate находится на вершине стека?

сценарий 2 : при выполнении @ attach_wrapper (обертка) управление переходит к выполнению attach_wrapper (obj, func = None) и частичная функция возвращается func =

вопрос : почему элемент управления возвращается для выполнения def attach_wrapper (obj, func = None): и как бы на этот раз значение для func было * .decorate..set_message в 0x000000000> передается в attach_wrapper?

1 Ответ

0 голосов
/ 05 сентября 2018

Сценарий 1

Это:

@logged(logging.DEBUG)
def add(x, y):
    ....

такой же, как этот:

def add(x, y):
    ....
add = logged(logging.DEBUG)(add)

Обратите внимание, что там есть два вызова: сначала logged(logging.DEBUG) возвращает decorate, а затем вызывается decorate(add).

Сценарий 2

То же, что и в Сценарий 1 , это:

@attach_wrapper(wrapper)
def set_message(newmsg):
    ...

такой же, как этот:

def set_message(newmsg):
    ...
set_message = attach_wrapper(wrapper)(set_message)

Опять же, есть два вызова: сначала attach_wrapper(wrapper) возвращает объект partial, а затем вызывается partial(set_message).


Другими словами ...

logged и attach_wrapper не являются декораторами. Это функции, которые возвращают декораторы. Вот почему выполняется два вызова: один - функции, которая возвращает декоратор, а другой - сам декоратор.

...