Maison  >  Article  >  développement back-end  >  Mythe de l'ordre d'exécution du décorateur Python

Mythe de l'ordre d'exécution du décorateur Python

高洛峰
高洛峰original
2017-02-17 11:02:441059parcourir

Explorer l'ordre d'exécution de plusieurs décorateurs

Les décorateurs sont des outils utilisés par Python pour encapsuler des fonctions ou des codes. Vous pouvez trouver de nombreux articles sur Internet pour apprendre ce dont je veux parler ici. plusieurs décorateurs. Un mythe sur la séquence.

Questions

La plupart des séquences d'appel de fonctions impliquant plusieurs décorateurs indiqueront qu'elles sont descendantes, comme dans l'exemple suivant :

def decorator_a(func):
    print 'Get in decorator_a'
    def inner_a(*args, **kwargs):
        print 'Get in inner_a'
        return func(*args, **kwargs)
    return inner_a

def decorator_b(func):
    print 'Get in decorator_b'
    def inner_b(*args, **kwargs):
        print 'Get in inner_b'
        return func(*args, **kwargs)
    return inner_b

@decorator_b
@decorator_a
def f(x):
    print 'Get in f'
    return x * 2

f(1)

Le code ci-dessus définit d'abord deux fonctions : decotator_a, decotator_b. La fonction de ces deux fonctions est de recevoir une fonction en paramètre puis de renvoyer une autre fonction créée. La fonction reçue est appelée dans cette fonction créée (le texte est plus alambiqué que le code). La dernière fonction définie f utilise decotator_a, decotator_b définis ci-dessus comme fonctions de décoration. Après avoir appelé la fonction décorée f avec 1 comme paramètre, quel est l'ordre de decotator_a, decotator_b (ici, afin d'indiquer l'ordre d'exécution de la fonction, la sortie d'impression est utilisée pour afficher l'ordre d'exécution de la fonction) ?

Si vous jugez sur la base du principe ascendant sans réfléchir, exécutez d'abord decorator_a, puis decorator_b, puis Get in decorator_a, Get in inner_a sera affiché en premier, puis Get in decorator_b, Get in inner_b. Mais ce n’est pas le cas.

Les résultats réels en cours d'exécution sont les suivants :

Get in decorator_a
Get in decorator_b
Get in inner_b
Get in inner_a
Get in f

La différence entre les fonctions et les appels de fonction

Pourquoi inner_b est-il exécuté en premier, puis inner_a ? Afin de bien comprendre le problème ci-dessus, il faut d’abord distinguer deux concepts : les fonctions et les appels de fonction. Dans l'exemple ci-dessus, f est appelé une fonction et f(1) est appelé un appel de fonction. Ce dernier est le résultat de l'évaluation des paramètres transmis par le premier. En Python, une fonction est aussi un objet, donc f fait référence à un objet fonction, et sa valeur est la fonction elle-même. f(1) est un appel à la fonction, et sa valeur est le résultat de l'appel. définition ici, f(1) La valeur est 2. De même, en prenant la fonction decorator_a ci-dessus comme exemple, elle renvoie un objet fonction inner_a, qui est défini en interne. La fonction func est appelée dans inner_a et le résultat de l'appel à func est renvoyé sous forme de valeur.

La fonction décorateur est exécutée immédiatement après la définition de la fonction décorée.

La deuxième question qui doit être clarifiée est de savoir ce qui se passe exactement lorsque le décorateur décore une fonction. Simplifions maintenant notre exemple, en supposant qu'il soit le suivant :

def decorator_a(func):
    print 'Get in decorator_a'
    def inner_a(*args, **kwargs):
        print 'Get in inner_a'
        return func(*args, **kwargs)
    return inner_a

@decorator_a
def f(x):
    print 'Get in f'
    return x * 2

Comme dit dans de nombreux articles présentant les décorateurs :

@decorator_a
def f(x):
    print 'Get in f'
    return x * 2

# 相当于
def f(x):
    print 'Get in f'
    return x * 2

f = decorator_a(f)

Donc, lorsque l'interpréteur exécute ce code Quand, decorator_a a été appelé, il prend la fonction f comme paramètre et renvoie une fonction générée en interne, donc par la suite f fait référence à inner_a renvoyé dans decorator_a. Ainsi, lorsque f sera appelé dans le futur, cela équivaut en fait à appeler inner_a. Les paramètres passés à f seront transmis à inner_a, les paramètres reçus seront transmis à la fonction inner_a, c'est-à-dire f. le retour final est celui appelé par la valeur f, donc à l'extérieur, cela ressemble à appeler à nouveau f directement.

Explication des questions

Lorsque les deux concepts ci-dessus sont clarifiés, vous pouvez clairement voir ce qui s'est passé dans l'exemple le plus original.
Lorsque l'interpréteur exécute le code suivant, il appelle en fait decorator_a et decorator_b dans l'ordre de bas en haut, ce qui affichera les Get in decorator_a et Get in decorator_b correspondants. À ce moment, f est équivalent à inner_b dans decorator_b. Mais comme f n'a pas été appelé, inner_b n'a pas été appelé, et par analogie, inner_a à l'intérieur de inner_b n'a pas été appelé, donc Get in inner_a et Get in inner_b ne seront pas affichés.

@decorator_b
@decorator_a
def f(x):
    print 'Get in f'
    return x * 2

Ensuite, dans la dernière ligne, lorsque nous appelons f avec le paramètre 1, inner_b est appelé. Il imprimera d'abord Get in inner_b, puis appellera inner_a à l'intérieur de inner_b, donc Get in sera à nouveau imprimé. inner_a, puis appelle l'original f en interne dans inner_a et renvoie le résultat comme résultat final. À ce stade, vous devez savoir pourquoi le résultat est ainsi et avoir une certaine compréhension de ce qui se passe réellement dans la séquence d'exécution du décorateur.

Lorsque nous supprimons l'appel de f dans la dernière ligne de l'exemple ci-dessus et le mettons dans le repl pour démonstration, nous pouvons aussi naturellement voir le problème d'ordre :

➜  test git:(master) ✗ python
Python 2.7.11 (default, Jan 22 2016, 08:29:18)
[GCC 4.2.1 Compatible Apple LLVM 7.0.2 (clang-700.1.81)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import test13
Get in decorator_a
Get in decorator_b
>>> test13.f(1)
Get in inner_b
Get in inner_a
Get in f
2
>>> test13.f(2)
Get in inner_b
Get in inner_a
Get in f
4
>>>

En réalité Scénarios d'application, lorsque nous écrivons deux méthodes de décoration de la manière ci-dessus, comme vérifier d'abord s'il y a une connexion @login_required, puis vérifier si les autorisations sont suffisantes @permision_allowed, nous utilisons l'ordre suivant pour décorer la fonction :

@login_required
@permision_allowed
def f()
  # Do something
  return

Pour plus d'articles liés aux mythes sur l'ordre d'exécution du décorateur Python, veuillez faire attention au site Web PHP chinois !

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn