如何在派生的python类上操作装饰器?

Posted

技术标签:

【中文标题】如何在派生的python类上操作装饰器?【英文标题】:How to operate decorator on derived python class? 【发布时间】:2013-03-23 16:09:28 【问题描述】:

我想使用装饰器对派生类做一些事情(例如注册类或其他东西)。这是我的代码:

from functools import wraps

class Basic(object):
    def __init__(self):
        print "Basic::init"

def myDeco(name):
    # define the decorator function that acts on the actual class definition
    def __decorator(myclass):

        # do something here with the information
        print name, myclass

        # do the wrapping of the class 
        @wraps(myclass)
        def __wrapper(*args, **kwargs):
            return myclass( *args, **kwargs)

        # return the actual wrapper here
        return __wrapper

    # return the decorator to act on the class definition
    return __decorator

@myDeco("test")
class Derived(Basic):
    def __init__(self):
        super(Derived, self).__init__()
        print "Derived::init"


instance = Derived()

这给出了以下错误:

TypeError: must be type, not function

Derived 中的super 方法被调用时。我假设变量Derived 不再是type,而是函数__decorator 实际上。

我需要如何更改装饰器(并且只有装饰器)才能解决此问题?

【问题讨论】:

装饰器内的装饰器 - 它自找麻烦... @JakubM.: 不,这个函数是一个装饰器工厂。这是常见的做法 @wrapsmyDeco 内?对我来说看起来很奇怪 IMO,在这种情况下,__wrapper 毫无意义。对于__decorator,可以直接返回myclass 【参考方案1】:

您正在用函数替换装饰类,因此类定义失败。

具体来说,super(Derived, self).__init__() 现在将一个函数传递给super()

>>> super(Derived, object())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: must be type, not function
>>> type(Derived)
<type 'function'>

类装饰器通常会更改类,添加、删除或更改其属性,然后再次返回类对象。

您应该Derived 替换为新类,因为您的Derived.__init__() 按名称引用自身。用另一个类替换该名称会导致痛苦(例如无限递归)。

确实工作的示例类装饰器:

def my_deco(name):
    # define the decorator function that acts on the actual class definition
    def decorator(cls):

        # do something here with the information
        print name, cls

        setattr(class, name, 'Hello world!')

        # return the original, augmented class
        return cls

    # return the decorator to act on the class definition
    return decorator

【讨论】:

【参考方案2】:

您的程序中有两个错误:

from functools import wraps

class Basic(object):
    def __init__(self):
        print "Basic::init"

def myDeco(name):
    # define the decorator function that acts on the actual class definition
    def __decorator(myclass):

        # do something here with the information
        print name, myclass

        # do the wrapping of the class
        class Wrap(myclass): # error 1
            realSuper = myclass # I dislike this. has anyone an other aproach
            def __new__(*args, **kwargs):
                return myclass.__new__(*args, **kwargs)

        # return the actual wrapper here
        return Wrap

    # return the decorator to act on the class definition
    return __decorator

@myDeco("test")
class Derived(Basic):
    def __init__(self):
        super(Derived.realSuper, self).__init__() # error 2
        print "Derived::init"


instance = Derived()

现在给出以下输出:

test <class '__main__.Derived'>
Basic::init
Derived::init

首先你需要一个其他人指出的类。

第二个super(Derived, self).__init__()造成了无限递归:

Traceback (most recent call last):
  File "<pyshell#8>", line 1, in <module>
    exec s
  File "<string>", line 32, in <module>
  File "<string>", line 28, in __init__
  File "<string>", line 28, in __init__
  File "<string>", line 28, in __init__
  File "<string>", line 28, in __init__

原因参见下面的讨论。

【讨论】:

不,super(Derived, self)正确的。 Python 需要以 current 类作为起点来查找 MRO 中的下一个类。通过传入Basic,您可以明确地skip that MRO 中的类。 请解释发生了什么错误,为什么是错误,以及如何纠正。更正后的代码本身就是一个糟糕的答案。 @Martijn Pieters 请告诉我,如果我不更改它,为什么它会在这一行中无限递归。 您的Wrap 将导致无限递归。 @User:你不能用它自己的超类替换一个类。如果您需要将类行为更改为该扩展,请使用元类。使用类装饰器只改变属性,不返回替换类。

以上是关于如何在派生的python类上操作装饰器?的主要内容,如果未能解决你的问题,请参考以下文章

如何让我的类装饰器只在继承链中的最外层类上运行?

如何使用带有参数的python装饰器?

Python装饰器详解

python装饰器

在基类和派生类中使用基类的装饰器

GetIt/Injectable 在抽象类上缺少可注入装饰器?