Как должны вести себя несколько экземпляров класса C
? Должен ли instance_method
вызываться только один раз, независимо от того, какой экземпляр вызывает function
? Или каждый экземпляр должен вызывать instance_method
один раз?
Ваш called=[]
аргумент по умолчанию заставляет декоратора помнить, что было вызвано что-то с именем строки function
Что если decorator
используется в двух разных классах, у каждого из которых есть метод с именем function
? Тогда
c=C()
d=D()
c.function()
d.function()
вызовет только c.instance_method
и предотвратит вызов d.instance_method
. Странно, и, вероятно, не то, что вы хотите.
Ниже я использую self._instance_method_called
для записи, если self.instance_method
был вызван. Это делает каждый экземпляр C
call instance_method
не более одного раза.
Если вы хотите, чтобы instance_method
вызывался не более одного раза, независимо от того, какой экземпляр C
вызывает function
, тогда просто определите _instance_method_called
как атрибут класса вместо атрибута экземпляра.
def decorator():
print('Locals in decorator %s ' % locals())
def wrap(f):
def wrapped(self,*args):
print('Locals in wrapper %s' % locals())
if not self._instance_method_called:
self.instance_method()
self._instance_method_called=True
return f
return wrapped
return wrap
class C:
def __init__(self):
self._instance_method_called=False
def instance_method(self): print('Method called')
@decorator()
def function(self):
pass
c = C()
# Locals in decorator {}
c.function()
# Locals in wrapper {'self': <__main__.C instance at 0xb76f1aec>, 'args': (), 'f': <function function at 0xb76eed14>}
# Method called
c.function()
# Locals in wrapper {'self': <__main__.C instance at 0xb76f1aec>, 'args': (), 'f': <function function at 0xb76eed14>}
d = C()
d.function()
# Locals in wrapper {'self': <__main__.C instance at 0xb76f1bcc>, 'args': (), 'f': <function function at 0xb76eed14>}
# Method called
d.function()
# Locals in wrapper {'self': <__main__.C instance at 0xb76f1bcc>, 'args': (), 'f': <function function at 0xb76eed14>}
Редактировать: Чтобы избавиться от оператора if
:
def decorator():
print('Locals in decorator %s ' % locals())
def wrap(f):
def rest(self,*args):
print('Locals in wrapper %s' % locals())
return f
def first(self,*args):
print('Locals in wrapper %s' % locals())
self.instance_method()
setattr(self.__class__,f.func_name,rest)
return f
return first
return wrap
class C:
def instance_method(self): print('Method called')
@decorator()
def function(self):
pass