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