在Python3中,用类装饰器包装构造函数的正确方法,使其也适用于不可变类型

在Python3中,用类装饰器包装构造函数的正确方法,使其也适用于不可变类型,python,python-3.x,decorator,Python,Python 3.x,Decorator,我试图定义一个类装饰器,它(除其他外)用一些自定义代码包装构造函数 我使用的是类修饰符,而不是继承,因为我希望修饰符适用于类层次结构中的多个类,并且我希望为层次结构中的每个装饰类执行包装代码 我可以这样做: def decorate(klass): def Dec(klass): def __init__(self,*a,**k): # wrapping code here super().__init__(*a,**k)

我试图定义一个类装饰器,它(除其他外)用一些自定义代码包装构造函数

我使用的是类修饰符,而不是继承,因为我希望修饰符适用于类层次结构中的多个类,并且我希望为层次结构中的每个装饰类执行包装代码

我可以这样做:

def decorate(klass):
    def Dec(klass):
        def __init__(self,*a,**k):
            # wrapping code here
            super().__init__(*a,**k)
            # wrapping code here
    return Dec
对于简单的测试用例,它工作得很好。但是,我担心用另一个类替换这个类可能会导致微妙的破坏(例如,如果一个装饰过的类决定做一些神秘的东西来引用它自己)。此外,它还破坏了类的良好默认repr字符串(它显示为“decoration..Dec”,而不是klass最初的名称)

我试着改变课堂本身:

def decorate(klass):
    old_init = klass.__init__
    def new_init(self,*a,**k):
        # wrapper code here
        old_init(self,*a,**k)
        # wrapper code here
    klass.__init__ = new_init
    return klass
这样,它就维护了正确的类名和所有内容,只要我的构造函数不带任何参数,它就可以正常工作。但是,当将其应用于例如
str
之类的类型时,它会中断:

@decorate
class S(str):
    pass


>>> s = S('foo')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "decorate.py", line 13, in new_init
    old_init(self,*a,**k)
TypeError: object.__init__() takes no parameters
然后

>>条=条(1)
回溯(最近一次呼叫最后一次):
文件“”,第1行,在
文件“decoration.py”,第12行,在new\u new中
i=旧的初始值(cls,*a,**k)
文件“decoration.py”,第12行,在new\u new中
i=旧的初始值(cls,*a,**k)
TypeError:对象()不接受任何参数
版本更改
\uuuu init\uuuu
如何可能失败?用另一个最通用的签名类型(*a,**k)替换一个函数并将调用代理给原始签名类型,怎么可能导致失败

为什么object.init似乎违反了至少接受一个位置参数的约定

如何使其在两种情况下都有效?我不能同时覆盖
\uuuuu init\uuuu
\uuuu new\uuuuu
来扩展相同的行为;应该使用哪个标准来动态地知道哪一个是正确的钩子


这真的应该以完全不同的方式实现吗,例如使用元类?

不可变类型没有
\uuuu init\uuuu
;他们使用
\uuuu new\uuuu
来生成不可变的实例。另一个问题不能完全回答这个问题。它只显示如何处理一个特定类(str),而不是如何编写在这两种情况下都正确的通用代码。它也没有解释在一个愚蠢的调用的一般代理上崩溃的反直觉行为;它没有说明是否应该/如何用一种完全不同的技术来实现,比如元类;它只是指出,在这里不能将可变基类和不可变基类一视同仁。您需要检测是否使用了
\uuuuu init\uuuu
\uuuu new\uuuuu
,并以此为基础进行装饰。哦,好吧,好吧。有没有一种干净的方法可以做到这一点?首先,决定是否真的需要支持不可变类型。为什么需要子类
str
并在该子类上使用decorator?你是在试图支持你能想象的一切,还是在解决一个特定的问题?
def decorate(klass):
    old_new = klass.__new__
    def new_new(cls,*a,**k):
        # wrapper code
        i = old_init(cls,*a,**k)
        # wrapper code
        return i

    klass.__new__ = new_new

return klass


@decorate
class Foo:
    pass

@decorate
class Bar(Foo):
    def __init__(self, x):
        self.x = x
>>> bar = Bar(1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "decorate.py", line 12, in new_new
    i = old_init(cls,*a,**k)
  File "decorate.py", line 12, in new_new
    i = old_init(cls,*a,**k)
TypeError: object() takes no parameters