96 votes

Comment dépouiller les décorateurs d'une fonction en Python

Disons que j'ai ce qui suit :

def with_connection(f):
    def decorated(*args, **kwargs):
        f(get_connection(...), *args, **kwargs)
    return decorated

@with_connection
def spam(connection):
    # Do something

Je veux tester la fonction spam sans passer par les tracas de la mise en place d'une connexion (ou quoi que le décorateur fasse).

Compte tenu de spam, comment retirer le décorateur et obtenir la fonction sous-jacente « non décorée » ?

57voto

balpha Points 18387

Dans le cas général, vous ne pouvez pas, parce que

@with_connection
def spam(connection):
    # Do something

est équivalent à

def spam(connection):
    # Do something

spam = with_connection(spam)

ce

qui signifie que le spam « original » pourrait même ne plus exister. Un (pas trop joli) hack serait ceci :

def with_connection(f):
    def decorated(*args, **kwargs):
        f(get_connection(...), *args, **kwargs)
    decorated._original = f
    return decorated

@with_connection
def spam(connection):
    # Do something

spam._original(testcon) # calls the undecorated function

34voto

jcdyer Points 7956

la solution de balpha peut être généralisée avec ce méta-décorateur :

def include_original(dec):
    def meta_decorator(f):
        decorated = dec(f)
        decorated._original = f
        return decorated
    return meta_decorator

Ensuite, vous pouvez décorer vos décorateurs avec @include_original, et chacun aura une version testable (non décorée) cachée à l'intérieur.

@include_original
def shout(f):
    def _():
        string = f()
        return string.upper()
    return _



@shout
def function():
    return "hello world"

>>> print function()
HELLO_WORLD
>>> print function._original()
hello world

13voto

Sophie Alpert Points 167

Vous pouvez maintenant utiliser le paquet non décoré :

>>> from undecorated import undecorated
>>> undecorated(spam)

Il passe par les tracas de creuser à travers toutes les couches de différents décorateurs jusqu'à ce qu'il atteigne la fonction de fond et ne nécessite pas de changer les décorateurs d'origine. Il fonctionne à la fois sur Python 2 et Python 3.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X