Декораторы применяются к определению класса с помощью Python - PullRequest
7 голосов
/ 05 октября 2010

По сравнению с декораторами, применяемыми к функции, непросто понять декораторы, применяемые к классу.

@foo
class Bar(object):
    def __init__(self, x):
        self.x = x
    def spam(self):
        statements

Какой вариант использования декораторов для класса?Как это использовать?

Ответы [ 2 ]

23 голосов
/ 05 октября 2010

Он заменяет подавляющее большинство классических хороших применений пользовательских метаклассов гораздо более простым способом.

Подумайте об этом так: ничто, непосредственно находящееся в теле класса, не может ссылаться на объект класса, потому что классобъект не существует до тех пор, пока тело не выполнит свою работу (задача метакласса создать объект класса - обычно type для всех классов без пользовательского метакласса).

Но кодв декораторе класса запускается после создается объект класса (действительно, с объектом класса в качестве аргумента!), и поэтому он вполне может ссылаться на этот объект класса (и обычно должен это делать).

Например, рассмотрим:

def enum(cls):
  names = getattr(cls, 'names', None)
  if names is None:
    raise TypeError('%r must have a class field `names` to be an `enum`!',
                    cls.__name__)
  for i, n in enumerate(names):
    setattr(cls, n, i)
  return cls

@enum
class Color(object):
  names = 'red green blue'.split()

, и теперь вы можете ссылаться на Color.red, Color.green, & c, а не на 0, 1 и т. Д. (Конечно,обычно вы добавляете еще больше функциональности, чтобы сделать «enum», но здесь я просто показываю простой способ добавить такую ​​функциональность в декоратор класса, а не использовать собственный метакласс! -)

6 голосов
/ 05 октября 2010

Один из вариантов использования, о котором я могу подумать, - это если вы хотите обернуть все методы класса одним декоратором функций.Скажем, у вас есть следующий декоратор:

def logit(f):
    def res(*args, **kwargs):
        print "Calling %s" % f.__name__
        return f(*args, **kwargs)
    return res

и следующий класс:

>>> class Pointless:
    def foo(self): print 'foo'
    def bar(self): print 'bar'
    def baz(self): print 'baz'

>>> p = Pointless()
>>> p.foo(); p.bar(); p.baz()
foo
bar
baz

Вы можете украсить все методы:

>>> class Pointless:
    @logit
    def foo(self): print 'foo'
    @logit
    def bar(self): print 'bar'
    @logit
    def baz(self): print 'baz'

>>> p = Pointless()
>>> p.foo(); p.bar(); p.baz()
Calling foo
foo
Calling bar
bar
Calling baz
baz

Но это LAME!Вместо этого вы можете сделать это:

>>> def logall(cls):
for a in dir(cls):
    if callable(getattr(cls, a)):
        setattr(cls, a, logit(getattr(cls, a)))
return cls

>>> @logall
class Pointless:
    def foo(self): print 'foo'
    def bar(self): print 'bar'
    def baz(self): print 'baz'

>>> p = Pointless()
>>> p.foo(); p.bar(); p.baz()
Calling foo
foo
Calling bar
bar
Calling baz
baz

ОБНОВЛЕНИЕ: более общая версия logall:

>>> def wrapall(method):
    def dec(cls):
        for a in dir(cls):
            if callable(getattr(cls, a)):
                setattr(cls, a, method(getattr(cls, a)))
        return cls
    return dec

>>> @wrapall(logit)
class Pointless:
        def foo(self): print 'foo'
        def bar(self): print 'bar'
        def baz(self): print 'baz'

>>> p = Pointless()
>>> p.foo(); p.bar(); p.baz()
Calling foo
foo
Calling bar
bar
Calling baz
baz
>>> 

Полное раскрытие: мне никогда не приходилось делать это, и я просто сделалэтот пример вверх.

...