2011-03-23 2 views
25

Existe-t-il un moyen de combiner deux décorateurs en un nouveau décorateur en python?Puis-je combiner deux décorateurs en un seul en Python?

Je me rends compte que je peux juste appliquer plusieurs décorateurs à une fonction, mais j'étais curieux de savoir s'il y avait un moyen simple de combiner deux dans un nouveau.

Répondre

39

Un peu plus général:

def composed(*decs): 
    def deco(f): 
     for dec in reversed(decs): 
      f = dec(f) 
     return f 
    return deco 

Puis

@composed(dec1, dec2) 
def some(f): 
    pass 

est équivalent à

@dec1 
@dec2 
def some(f): 
    pass 
+1

[S'il vous plaît pas d'espaces à l'intérieur de parens.] (Http://www.python.org/dev/peps/pep-0008/) – delnan

+1

'retour lambda x: réduire (lambda y, f: f (y), decs, x) '... bien, après avoir tapé cela, je vois l'avantage de votre code :) –

+1

Une autre chose que je viens de remarquer:' @composed (dec1, dec2) 'sera équivalent à' @ dec2 @ dec1 ', ce qui est au moins contre-intuitif. –

3

Si les décorateurs ne prennent pas d'arguments supplémentaires, vous pouvez utiliser

def compose(f, g): 
    return lambda x: f(g(x)) 

combined_decorator = compose(decorator1, decorator2) 

maintenant

@combined_decorator 
def f(): 
    pass 

sera équivalent à

@decorator1 
@decorator2 
def f(): 
    pass 
+0

N'est-ce pas "appliquer plusieurs décorateurs à une fonction"? – delnan

+0

@delnan: N'est-ce pas "un moyen simple de combiner deux [décorateurs] dans un nouveau"? :) –

+0

Merci. L'un des décorateurs prend params si bien que je suis parti avec l'autre réponse. – Ludo

17

Oui. Voir la définition d'un décorateur, here.

Quelque chose comme cela devrait fonctionner:

def multiple_decorators(func): 
    return decorator1(decorator2(func)) 

@multiple_decorators 
def foo(): pass 
+0

Merci, et aussi un lien utile. J'ai opté pour la réponse avec la solution plus générale. À votre santé. – Ludo

+0

J'aime la concision de cette solution et je l'ai trouvé utile dans mon projet. –

+0

** Idem. ** Bien que la réponse acceptée soit certainement impressionnante pour le cas général, cette réponse montre de façon concise un décorateur se reportant à plusieurs autres décorateurs dont les noms sont statiquement connus au moment de l'interprétation. Puisque c'est le cas habituel, c'est génial aussi! _Upvotes pour tous ensue._ –

1

Si vous ne t voulez-vous répéter trop dans une suite de tests, vous pouvez faire comme ceci ::

def apply_patches(func): 
    @functools.wraps(func) 
    @mock.patch('foo.settings.USE_FAKE_CONNECTION', False) 
    @mock.patch('foo.settings.DATABASE_URI', 'li://foo') 
    @mock.patch('foo.connection.api.Session.post', autospec=True) 
    def _(*args, **kwargs): 
     return func(*args, **kwargs) 

    return _ 

vous pouvez l'utiliser dans votre suite de test au lieu d'une quantité folle de décorateurs au-dessus de chaque function ::

def ChuckNorrisCase(unittest.TestCase): 
    @apply_patches 
    def test_chuck_pwns_none(self): 
     self.assertTrue(None) 
Questions connexes