Как я могу украсить все функции класса, не вводя его снова и снова для каждого добавленного метода? питон

Допустим, в моем классе есть много методов, и я хочу применить свой декоратор к каждому из них, позже, когда я добавлю новые методы, я хочу применить тот же декоратор, но я не хочу писать @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
Другие вопросы по тегам