web-dev-qa-db-fra.com

Ordre d'exécution du décorateur

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()

Sortie: "<b><i>hello world</i></b>"

Je comprends à peu près les décorateurs et comment cela fonctionne avec l'un d'eux dans la plupart des exemples.

Dans cet exemple, il y en a 2. De la sortie, il semble que @make_italic s'exécute d'abord, puis @make_bold.

Cela signifie-t-il que pour les fonctions décorées, il exécutera d'abord la fonction puis se déplacera vers le haut pour les autres décorateurs? Comme @make_italic d'abord, puis @make_bold, au lieu du contraire.

Cela signifie donc qu'il est différent de la norme de l'approche descendante dans la plupart des langages de programmation? Juste pour ce cas de décorateur? Ou ai-je tort?

70
Newbie

Décorateurs wrap la fonction qu'ils décorent. Ainsi make_bold A décoré le résultat du décorateur make_italic, Qui a décoré la fonction hello.

La syntaxe @decorator Est vraiment juste du sucre syntaxique; le suivant:

@decorator
def decorated_function():
    # ...

est vraiment exécuté comme:

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

remplacer l'objet decorated_function d'origine par ce que decorator() a renvoyé.

Empiler les décorateurs répète ce processus vers l'extérieur.

Donc votre échantillon:

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

peut être étendu à:

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

Lorsque vous appelez hello() maintenant, vous appelez vraiment l'objet renvoyé par make_bold(). make_bold() a renvoyé un lambda qui appelle la fonction make_bold encapsulée, qui est la valeur de retour de make_italic(), qui est également un lambda qui appelle l'original hello(). En élargissant tous ces appels, vous obtenez:

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

donc la sortie devient:

"<b>" + ("<i>" + ("hello world") + "</i>") + "</b>"
94
Martijn Pieters