装饰器使用Python应用于类定义

pro*_*eek 7 python decorator

与应用于函数的装饰器相比,理解应用于类的装饰器并不容易.

@foo
class Bar(object):
    def __init__(self, x):
        self.x = x
    def spam(self):
        statements
Run Code Online (Sandbox Code Playgroud)

装饰器对一个类的用例是什么?如何使用它?

Ale*_*lli 23

它以更简单的方式取代了定制元类的绝大多数经典优良用途.

以这种方式思考:直接在类体中的任何东西都不能引用类对象,因为类对象直到主体完成运行之后才存在(这是元类创建类对象的工作 - 通常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()
Run Code Online (Sandbox Code Playgroud)

现在你可以参考Color.red,Color.green,&C,而不是0,1等(当然,你通常会添加更多的功能,使"一个enum",但在这里我只是呈现简单的方式把一个这样的功能除了类装饰器,而不是需要自定义元类! - )


Cla*_*diu 6

我能想到的一个用例是,如果你想用一个函数装饰器包装一个类的所有方法.假设您有以下装饰者:

def logit(f):
    def res(*args, **kwargs):
        print "Calling %s" % f.__name__
        return f(*args, **kwargs)
    return res
Run Code Online (Sandbox Code Playgroud)

以下课程:

>>> 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
Run Code Online (Sandbox Code Playgroud)

你可以装饰所有的方法:

>>> 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
Run Code Online (Sandbox Code Playgroud)

但那就是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
Run Code Online (Sandbox Code Playgroud)

更新:更通用的版本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
>>> 
Run Code Online (Sandbox Code Playgroud)

完全披露:我从来没有这样做,我只是做了这个例子.