Dekoratorzy Pythona w klasach

Można napisać sth jak:

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

    @self._decorator
    def bar(self):
        pass

To się nie uda: self in @ self is unknown

Ja też próbowałem:

@Test._decorator(self)

Które również nie powiodło się: Test unknown

Jeśli chcesz pracować tymczasowo. zmiana niektórych zmiennych instancji w dekoratorze i w metodzie dekorowania, przed zmieniam je z powrotem. Dzięki.
Author: User97693321, 2009-08-12

9 answers

To, co chcesz zrobić, nie jest możliwe. Weźmy na przykład, czy poniższy kod wygląda na poprawny, czy nie:

class Test(object):

    def _decorator(self, foo):
        foo()

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

To, oczywiście, nie jest ważne, ponieważ self nie jest zdefiniowany w tym momencie. To samo dotyczy Test, ponieważ nie zostanie zdefiniowana, dopóki sama klasa nie zostanie zdefiniowana (która jest w procesie). Pokazuję ci ten fragment kodu, ponieważ w to przekształca się twój fragment dekoratora.

Więc, jak widzisz, dostęp do instancji w dekoratorze takim jak ten nie jest to tak naprawdę możliwe, ponieważ dekoratory są stosowane podczas definiowania dowolnej funkcji / metody, do której są dołączone, a nie podczas tworzenia instancji.

Jeśli potrzebujesz dostępu na poziomie klasy , spróbuj tego:

class Test(object):

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

    def bar(self):
        pass
Test.bar = Test._decorator(Test.bar)
 52
Author: Evan Fosmark,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-08-11 23:39:29

Czy coś takiego da ci to, czego potrzebujesz?

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

Pozwala to uniknąć wywołania self, aby uzyskać dostęp do dekoratora i pozostawia go ukrytego w przestrzeni nazw klasy jako regularna metoda.

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

Edytowano, aby odpowiedzieć na pytanie w komentarzach:

Jak używać ukrytego dekoratora w innej klasie

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
 191
Author: Michael Speer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-12-15 01:15:17
class Example(object):

    def wrapper(func):
        @functools.wraps(func)
        def wrap(self, *args, **kwargs):
            print "inside wrap"
            return func(self, *args, **kwargs)
        return wrap

    @wrapper
    def method(self):
        pass

    wrapper = staticmethod(wrapper)
 12
Author: madjardi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-17 12:48:55

Używam tego typu dekoratora w niektórych sytuacjach debugowania, pozwala on na nadpisanie właściwości klasy przez dekorowanie, bez konieczności znajdowania funkcji wywołującej.

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

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

Oto kod dekoratora

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 

Uwaga: ponieważ użyłem dekoratora klasy, musisz użyć @adecorator () z nawiasami do dekorowania funkcji, nawet jeśli nie przekazujesz żadnych argumentów konstruktorowi klasy dekoratora.

 5
Author: digitalacorn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-08-20 09:13:09

Jest to jeden ze sposobów, które znam (i użyłem), aby uzyskać dostęp self z wnętrza dekoratora zdefiniowanego wewnątrz tej samej klasy:

class Thing(object):
    def __init__(self, name):
        self.name = name

    def debug_name(function):
        def debug_wrapper(*args):
            self = args[0]
            print 'self.name = ' + self.name
            print 'running function {}()'.format(function.__name__)
            function(*args)
            print 'self.name = ' + self.name
        return debug_wrapper

    @debug_name
    def set_name(self, new_name):
        self.name = new_name

Wyjście (testowane na Pythonie 2.7.10):

>>> a = Thing('A')
>>> a.name
'A'
>>> a.set_name('B')
self.name = A
running function set_name()
self.name = B
>>> a.name
'B'

Powyższy przykład jest głupi, ale pokazuje, że działa.

 5
Author: Gunnar Hermansson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-07 09:43:25

Znalazłem to pytanie podczas badania bardzo podobny problem. Moim rozwiązaniem jest podzielenie problemu na dwie części. Najpierw musisz przechwycić dane, które chcesz powiązać z metodami klasy. W takim przypadku handler_for powiąże polecenie uniksowe z obsługą dla wyjścia tego polecenia.

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

Teraz, gdy powiązaliśmy niektóre dane z każdą metodą klasy, musimy zebrać te dane i zapisać je w atrybucie klasy.

OutputAnalysis.cmd_handler = {}
for value in OutputAnalysis.__dict__.itervalues():
    try:
        OutputAnalysis.cmd_handler[value.handler_for] = value
    except AttributeError:
        pass
 3
Author: samwyse,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-01-17 17:48:25

Oto rozwinięcie odpowiedzi Michaela Speera, aby pójść o kilka kroków dalej:

Dekorator metody instancji, który pobiera argumenty i działa na funkcji z argumentami i zwracaną wartością.

class Test(object):
    "Prints if x == y. Throws an error otherwise."
    def __init__(self, x):
        self.x = x

    def _outer_decorator(y):
        def _decorator(foo):
            def magic(self, *args, **kwargs) :
                print("start magic")
                if self.x == y:
                    return foo(self, *args, **kwargs)
                else:
                    raise ValueError("x ({}) != y ({})".format(self.x, y))
                print("end magic")
            return magic

        return _decorator

    @_outer_decorator(y=3)
    def bar(self, *args, **kwargs) :
        print("normal call")
        print("args: {}".format(args))
        print("kwargs: {}".format(kwargs))

        return 27

A następnie

In [2]:

    test = Test(3)
    test.bar(
        13,
        'Test',
        q=9,
        lollipop=[1,2,3]
    )
    ​
    start magic
    normal call
    args: (13, 'Test')
    kwargs: {'q': 9, 'lollipop': [1, 2, 3]}
Out[2]:
    27
In [3]:

    test = Test(4)
    test.bar(
        13,
        'Test',
        q=9,
        lollipop=[1,2,3]
    )
    ​
    start magic
    ---------------------------------------------------------------------------
    ValueError                                Traceback (most recent call last)
    <ipython-input-3-576146b3d37e> in <module>()
          4     'Test',
          5     q=9,
    ----> 6     lollipop=[1,2,3]
          7 )

    <ipython-input-1-428f22ac6c9b> in magic(self, *args, **kwargs)
         11                     return foo(self, *args, **kwargs)
         12                 else:
    ---> 13                     raise ValueError("x ({}) != y ({})".format(self.x, y))
         14                 print("end magic")
         15             return magic

    ValueError: x (4) != y (3)
 3
Author: Oliver Evans,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-10 00:10:38

Dekoratory wydają się lepiej przystosowane do modyfikowania funkcjonalności całego obiektu (w tym obiektów funkcyjnych) w porównaniu z funkcjonalnością metody obiektu , która ogólnie zależy od atrybutów instancji. Na przykład:

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"

Wyjście to:

>>> import Test
>>> a = Test()
>>> a.bar()
starting dec
bar
ending dec
 2
Author: nicodjimenez,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-27 02:22:25

Możesz udekorować dekoratora:

import decorator

class Test(object):
    @decorator.decorator
    def _decorator(foo, self):
        foo(self)

    @_decorator
    def bar(self):
        pass
 1
Author: doep,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-13 15:37:39