Замовлення на виконання декоратора


93
def make_bold(fn):
    return lambda : "<b>" + fn() + "</b>"

def make_italic(fn):
    return lambda : "<i>" + fn() + "</i>"

@make_bold
@make_italic
def hello():
  return "hello world"

helloHTML = hello()

Вихід: "<b><i>hello world</i></b>"

Я приблизно розумію декоратори та те, як це працює з одним із них у більшості прикладів.

У цьому прикладі їх 2. З вихідних даних здається, що @make_italicвиконується спочатку, а потім @make_bold.

Чи означає це, що для оформлених функцій вона спочатку запускає функцію, а потім рухається вгору для інших декораторів? Як @make_italicспочатку тоді @make_bold, а не навпаки.

Отже, це означає, що він відрізняється від норми підходу зверху вниз у більшості мов програмування? Тільки для цього випадку декоратора? Або я помиляюся?


4
так, починається знизу вгору, передаючи результат наступному
Падраїку Каннінгему

1
Коментар @PadraicCunningham також є важливою частиною відповіді. Якщо б споріднену задачу ( stackoverflow.com/questions/47042196 / ... )
shookees

Я б сказав, що це все ще зверху вниз, в тому сенсі, що a(b(x))зверху вниз (якщо ви уявляєте, що це розділено на 3 рядки)
Джоел

Відповіді:


126

Декоратори обгортають функцію, яку вони прикрашають. Так make_boldоформив результат make_italicдекоратор, який прикрасив helloфункцію.

@decoratorСинтаксис дійсно просто синтаксичний цукор; наступне:

@decorator
def decorated_function():
    # ...

дійсно виконується як:

def decorated_function():
    # ...
decorated_function = decorator(decorated_function)

заміна оригінального decorated_functionоб'єкта тим, що decorator()повернуто.

Декоратори укладання повторюють цей процес назовні .

Отже, ваш зразок:

@make_bold
@make_italic
def hello():
  return "hello world"

можна розширити до:

def hello():
  return "hello world"
hello = make_bold(make_italic(hello))

Коли ви телефонуєте hello()зараз, ви викликаєте об'єкт, повернутий make_bold(), насправді. make_bold()повернув a, lambdaякий викликає функцію в make_boldзагорнутому вигляді, що є повернутим значенням make_italic(), яке також є лямбда-сигналом, який викликає оригінал hello(). Розширюючи всі ці дзвінки, ви отримуєте:

hello() = lambda : "<b>" + fn() + "</b>" #  where fn() ->
    lambda : "<i>" + fn() + "</i>" # where fn() -> 
        return "hello world"

таким чином, результат стає:

"<b>" + ("<i>" + ("hello world") + "</i>") + "</b>"

Я розумію. Але чи означає це, що коли в цьому випадку є 2 обгортки, IDE автоматично виявить і оберне результат першої обгортки? Бо я так думав @make_bold #make_bold = make_bold(hello) @make_italic #make_italic = make_italic (hello)? Я не впевнений, що на основі цього він оберне перший результат. Або для цього випадку з 2 обгортками IDE використовуватиме те make_bold(make_italic(hello)), що ви вже згадали, замість того, що я поділився?
Новачок

3
@Newbie: Ваш IDE тут нічого не робить; це Python , що робить упаковку. Я показав вам у своєму останньому зразку, який make_bold()обертає висновок make_italic(), який був використаний для обтікання hello, тобто еквівалент make_bold(make_italic(hello)).
Мартін Пітерс

Не могли б ви надати версію цього коду без використання лямбда-сигналу? Я спробував .format, але не працює. І чому в цьому прикладі використовується лямбда? Я намагаюся зрозуміти лямбда і як це працює в цьому прикладі, але все ще маю проблеми. Я розумію, що лямбда - це як функції одного рядка, які можна передавати набагато легше порівняно з нормою функцій def?
Новачок

def inner: return "<b>" + fn() + "</b>", тоді return innerце буде "звичайна" версія функції; не така велика різниця.
Мартін Пітерс

Я завжди плутаюся в порядку. "... декоратори будуть застосовуватися, починаючи з найближчого до висловлення" def "" Я називаю це "навиворіт". Я думаю, що Мартейн називає це "зовнішнім". Це означає, що make_italic декоратор виконується перед make_bold декоратором , оскільки make_italicвін найближчий до def. Однак я забуваю, що порядок виконання декорованого коду: make_bold декорований (тобто напівжирний лямбда) виконується спочатку, а потім make_italic оформлений лямбда (тобто курсив лямбда).
Червоний горох,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.