Как я могу украсить все функции класса, не вводя его снова и снова для каждого добавленного метода? питон
Допустим, в моем классе есть много методов, и я хочу применить свой декоратор к каждому из них, позже, когда я добавлю новые методы, я хочу применить тот же декоратор, но я не хочу писать @mydecorator над объявлением метода время?
Если я смотрю в __call__
это правильный путь?
Спасибо
ВАЖНО: приведенный ниже пример, похоже, решает проблему, отличную от первоначального вопроса.
РЕДАКТИРОВАТЬ: Я хотел бы показать этот путь, который является аналогичным решением моей проблемы для любого, кто не найдет этот вопрос позже, используя миксин, как упомянуто в комментариях.
class WrapinMixin(object):
def __call__(self, hey, you, *args):
print 'entering', hey, you, repr(args)
try:
ret = getattr(self, hey)(you, *args)
return ret
except:
ret = str(e)
raise
finally:
print 'leaving', hey, repr(ret)
Тогда можешь в другом
class Wrapmymethodsaround(WrapinMixin):
def __call__:
return super(Wrapmymethodsaround, self).__call__(hey, you, *args)
5 ответов
Украсьте класс функцией, которая просматривает атрибуты класса и украшает вызываемые элементы. Это может быть неправильно, если у вас есть переменные класса, которые могут вызываться, и они также украсят вложенные классы (благодарность Свену Марнаху за указание на это), но в целом это довольно чистое и простое решение. Пример реализации (обратите внимание, что это не исключает специальных методов (__init__
и т.д.), что может или не может быть желательным):
def for_all_methods(decorator):
def decorate(cls):
for attr in cls.__dict__: # there's propably a better way to do this
if callable(getattr(cls, attr)):
setattr(cls, attr, decorator(getattr(cls, attr)))
return cls
return decorate
Используйте как это:
@for_all_methods(mydecorator)
class C(object):
def m1(self): pass
def m2(self, x): pass
...
В Python 3.0 и 3.1 callable
не существует. Он существует вечно в Python 2.x и возвращается в Python 3.2 в качестве оболочки для isinstance(x, collections.Callable)
, так что вы можете использовать это (или определить свой собственный callable
замена с помощью этого) в этих версиях.
Хотя я не люблю использовать магические подходы, когда подходит явный подход, вы, вероятно, можете использовать метакласс для этого.
def myDecorator(fn):
fn.foo = 'bar'
return fn
class myMetaClass(type):
def __new__(cls, name, bases, local):
for attr in local:
value = local[attr]
if callable(value):
local[attr] = myDecorator(value)
return type.__new__(cls, name, bases, local)
class myClass(object):
__metaclass__ = myMetaClass
def baz(self):
print self.baz.foo
и это работает, как будто каждый вызываемый в myClass
был украшен myDecorator
>>> quux = myClass()
>>> quux.baz()
bar
Не для того, чтобы оживить вещи из мертвых, но мне очень понравился ответ Делнана, но я обнаружил, что его все еще не хватает.
def for_all_methods(exclude, decorator):
def decorate(cls):
for attr in cls.__dict__:
if callable(getattr(cls, attr)) and attr not in exclude:
setattr(cls, attr, decorator(getattr(cls, attr)))
return cls
return decorate
РЕДАКТИРОВАТЬ: исправить отступ
Таким образом, вы можете указать методы // атрибуты // вещи, которые вы не хотите украшать
Ни один из приведенных выше ответов не помог мне, так как я хотел также унаследовать унаследованные методы, чего не удалось добиться с помощью __dict__
и я не хотел слишком усложнять вещи метаклассами. Наконец, у меня все в порядке с решением для Python 2, поскольку мне просто необходимо добавить некоторый код профилирования для измерения времени, используемого всеми функциями класса.
import inspect
def for_all_methods(decorator):
def decorate(cls):
for name, fn in inspect.getmembers(cls, inspect.ismethod):
setattr(cls, name, decorator(fn))
return cls
return decorate
Источник (немного другое решение): /questions/19167238/prisoedinenie-dekoratora-ko-vsem-funktsiyam-v-klasse/19167260#19167260 Там же вы можете увидеть, как изменить его для Python 3.
Как предлагают комментарии к другим ответам, рассмотрите возможность использования inspect.getmembers(cls, inspect.isroutine)
вместо. Если вы нашли правильное решение, которое работает как для Python 2, так и для Python 3 и украшает унаследованные методы, и все еще может быть выполнено в 7 строк, пожалуйста, отредактируйте.
Вы можете сгенерировать метакласс. Это не будет украшать унаследованные методы.
def decorating_meta(decorator):
class DecoratingMetaclass(type):
def __new__(self, class_name, bases, namespace):
for key, value in list(namespace.items()):
if callable(value):
namespace[key] = decorator(value)
return type.__new__(self, class_name, bases, namespace)
return DecoratingMetaclass
Это сгенерирует метакласс, украшающий все методы с указанной функцией. Вы можете использовать его в Python 2 или 3, выполнив что-то вроде этого
def doubling_decorator(f):
def decorated(*a, **kw):
return f(*a, **kw) * 2
return decorated
class Foo(dict):
__metaclass__ = decorating_meta(doubling_decorator)
def lookup(self, key):
return self[key]
d = Foo()
d["bar"] = 5
print(d.lookup("bar")) # prints 10