2010-10-05 64 views
7

与应用于函数的修饰器相比,理解应用于类的修饰器并不容易。适用于使用Python定义类的修饰器

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

装饰器的用途是什么?如何使用它?

回答

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() 

,现在你可以参考Color.redColor.green,& C,而不是01等(当然,你通常会添加更多的功能,使“enum”,但在这里我只是展示了将类功能添加到类装饰器中的简单方法,而不需要定制元类!)

+0

@Manoj,很高兴你喜欢它,tx让我知道! – 2010-10-05 05:31:23

6

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

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 

但是,这是个馊主意?相反,你可以这样做:

>>> 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 

UPDATE:的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 
>>> 

全面披露:我从来没有做到这一点,我刚才提出这个例子了。