【问题标题】:Decorators applied to class definition with Python使用 Python 应用于类定义的装饰器
【发布时间】:2010-10-05 02:51:27
【问题描述】:

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

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

装饰器对类的用例是什么?怎么用?

【问题讨论】:

    标签: python decorator


    【解决方案1】:

    它以更简单的方式取代了自定义元类的绝大多数经典用途。

    这样想:直接在类主体中的任何东西都不能引用类对象,因为类对象在主体完成运行之后才存在(创建类对象是元类的工作——通常是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” ,但在这里我只是展示了将此类功能添加到类装饰器中的简单方法,而不是需要自定义元类!-)

    【讨论】:

    • @Manoj,很高兴你喜欢它,谢谢让我知道!
    【解决方案2】:

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

    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
    

    更新: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
    >>> 
    

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-01-01
      • 2016-03-17
      • 2021-09-12
      • 1970-01-01
      • 1970-01-01
      • 2020-11-30
      • 2021-07-16
      • 2015-02-01
      相关资源
      最近更新 更多