Python Украсить все методы подкласса и предоставить средства для переопределения - PullRequest
6 голосов
/ 23 ноября 2011

Я работаю над поиском способа уменьшить количество шаблонных декораторов. У нас есть много классов, которые используют @decorate. Например:

class MyClass(Base):
     @decorate
     def fun1(self):
         pass
     @decorate
     def fun2(self):
         pass
     def fun3(self):
         pass

Я хочу сделать так, чтобы по умолчанию там был декоратор, если кто-то не укажет иначе.


Я использую этот код для автоматического переноса

from functools import wraps

def myDecorator(func):
    @wraps(func)
    def decorator(self, *args, **kwargs):
        try:
            print 'enter'
            ret = func(self, *args, **kwargs)
            print 'leave'
        except:
            print 'exception'
            ret = None

        return ret

    return decorator

class TestDecorateAllMeta(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 TestClass(object):
    __metaclass__ = TestDecorateAllMeta

    def test_print2(self, val):
        print val

    def test_print(self, val):
        print val

c = TestClass()
c.test_print1("print 1")
c.test_print2("print 2")

Мой вопрос:

  1. Есть ли лучший способ выполнить авто-декорирование?
  2. Как мне переопределить?

В идеале, мое конечное решение должно выглядеть примерно так:

class TestClass(object):
    __metaclass__ = TestDecorateAllMeta

    def autowrap(self):
        print("Auto wrap")

    @dont_decorate
    def test_dont_decorate(self, val):
        print val

Редактировать

Поговорить с одним из комментариев ниже, поскольку класс вызывается вместо выполнения

if callable(value):

Следует читать:

if isinstance(value,types.FunctionType) 

1 Ответ

3 голосов
/ 23 ноября 2011

Вместо того, чтобы заставлять пользователя моего класса указывать атрибут __metaclass__, я бы просто получил его от моего базового класса, который его определяет.Не нужно подвергать сантехнику излишне.

Кроме того, выглядит хорошо, хотя.Ваш @dont_decorate функциональный декоратор может быть реализован путем установки атрибута в исходной функции, который ваш декоратор класса затем обнаруживает и пропускает декорирование, если оно присутствует.

def dont_decorate(func):
    func._dont_decorate = True
    return func

Затем в вашем метаклассе, где вы сейчаспросто введите строку if callable(value)::

if callable(value) and not hasttr(value, "_dont_decorate"):

Кроме того, классы могут вызываться , поэтому, если вы не хотите, чтобы внутренние классы были оформлены, вам, вероятно, следует проверить функции с помощью isinstance() вместо callable().

Если вас интересуют более явные альтернативы, вы можете взглянуть на этот недавний вопрос , где кто-то хотел сделать по существу то же самое, используя классдекоратор.К сожалению, это немного сложнее, потому что методы уже обернуты к тому времени, когда их увидит декоратор.

...