225 votes

Python décorateurs dans les classes

Peut-on écrire qqch comme:

class Test(object):
    def _decorator(self, foo):
        foo()

    @self._decorator
    def bar(self):
        pass

Cette échoue: l'auto dans @auto est inconnu

J'ai aussi essayé:

@Test._decorator(self)

qui échoue également: Test de l'inconnu

Si souhaitez temp. modifier certaines variables d'instance dans le décorateur et l'exécuter décoré de la méthode, avant de changer l'arrière.

Merci.

372voto

Michael Speer Points 911

Serait quelque chose comme cela de faire ce dont vous avez besoin?

class Test(object):
    def _decorator(foo):
        def magic( self ) :
            print "start magic"
            foo( self )
            print "end magic"
        return magic

    @_decorator
    def bar( self ) :
        print "normal call"

test = Test()

test.bar()

Cela évite l'appel à l'autonomie pour accéder à la décoratrice et laisse cachés dans la classe de l'espace de noms que d'une méthode régulière.

>>> import stackoverflow
>>> test = stackoverflow.Test()
>>> test.bar()
start magic
normal call
end magic
>>> 

édité pour répondre à la question dans les commentaires:

Comment utiliser le caché décorateur dans une autre classe

class Test(object):
    def _decorator(foo):
        def magic( self ) :
            print "start magic"
            foo( self )
            print "end magic"
        return magic

    @_decorator
    def bar( self ) :
        print "normal call"

    _decorator = staticmethod( _decorator )

class TestB( Test ):
    @Test._decorator
    def bar( self ):
        print "override bar in"
        super( TestB, self ).bar()
        print "override bar out"

print "Normal:"
test = Test()
test.bar()
print

print "Inherited:"
b = TestB()
b.bar()
print

65voto

Evan Fosmark Points 17732

Ce que vous êtes désireux de le faire n'est pas possible. Prenez, par exemple, de savoir si ou non le code ci-dessous est valide:

class Test(object):

    def _decorator(self, foo):
        foo()

    def bar(self):
        pass
    bar = self._decorator(bar)

Elle, bien sûr, n'est pas valide puisqu' self n'est pas défini à ce stade. Il en va de même pour l' Test qu'il ne sera pas défini jusqu'à ce que la classe elle-même est définie (ce qui son dans le processus d'). Je vous montre cet extrait de code, parce que c'est ce que votre décorateur d'extrait de transforme en.

Donc, comme vous pouvez le voir, l'accès à l'instance dans un décorateur, comme ce n'est pas vraiment possible, car les décorateurs sont appliquées lors de la définition de ce que la fonction/méthode qu'ils y sont attachés, et non lors de l'instanciation.

Si vous avez besoin de la classe au niveau de l'accès, essayez ceci:

class Test(object):

    @classmethod
    def _decorator(cls, foo):
        foo()

    def bar(self):
        pass
Test.bar = Test._decorator(Test.bar)

7voto

digitalacorn Points 46

J'utilise ce type de décorateur dans un peu de débogage situations, il permet primordial des propriétés de la classe par la décoration, sans avoir à trouver la fonction appelante.

class myclass(object):
    def __init__(self):
        self.property = "HELLO"

    @adecorator(property="GOODBYE")
    def method(self):
        print self.property

Voici le décorateur code

class adecorator (object):
    def __init__ (self, *args, **kwargs):
        # store arguments passed to the decorator
        self.args = args
        self.kwargs = kwargs

    def __call__(self, func):
        def newf(*args, **kwargs):

            #the 'self' for a method function is passed as args[0]
            slf = args[0]

            # replace and store the attributes
            saved = {}
            for k,v in self.kwargs.items():
                if hasattr(slf, k):
                    saved[k] = getattr(slf,k)
                    setattr(slf, k, v)

            # call the method
            ret = func(*args, **kwargs)

            #put things back
            for k,v in saved.items():
                setattr(slf, k, v)

            return ret
        newf.__doc__ = func.__doc__
        return newf 

Remarque: parce que j'ai utilisé une classe décorateur, vous aurez besoin d'utiliser @adecorator() avec les supports pour décorer les fonctions, même si vous ne passez pas tous les arguments pour le décorateur le constructeur de la classe.

7voto

samwyse Points 435

J'ai trouvé cette question alors que des recherches sur un problème très similaire. Ma solution est de diviser le problème en deux parties. Tout d'abord, vous avez besoin pour capturer les données que vous souhaitez associer avec les méthodes de la classe. Dans ce cas, handler_for va associer une commande Unix avec le gestionnaire pour que la sortie de la commande.

class OutputAnalysis(object):
    "analyze the output of diagnostic commands"
    def handler_for(name):
        "decorator to associate a function with a command"
        def wrapper(func):
            func.handler_for = name
            return func
        return wrapper
    # associate mount_p with 'mount_-p.txt'
    @handler_for('mount -p')
    def mount_p(self, slurped):
        pass

Maintenant que nous avons associé des données avec chaque méthode de la classe, nous avons besoin de recueillir des données et de les stocker dans un attribut de classe.

OutputAnalysis.cmd_handler = {}
for value in OutputAnalysis.__dict__.itervalues():
    try:
        OutputAnalysis.cmd_handler[value.handler_for] = value
    except AttributeError:
        pass

4voto

nicodjimenez Points 66

Les décorateurs semblent mieux adaptés pour modifier la fonctionnalité d'un objet entier (y compris les objets de fonction) par rapport à la fonctionnalité d'une méthode de l'objet qui, en général, dépend de l'instance attributs. Par exemple:

def mod_bar(cls):
    # returns modified class

    def decorate(fcn):
        # returns decorated function

        def new_fcn(self):
            print self.start_str
            print fcn(self)
            print self.end_str

        return new_fcn

    cls.bar = decorate(cls.bar)
    return cls

@mod_bar
class Test(object):
    def __init__(self):
        self.start_str = "starting dec"
        self.end_str = "ending dec" 

    def bar(self):
        return "bar"

La sortie est:

>>> import Test
>>> a = Test()
>>> a.bar()
starting dec
bar
ending dec

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