Python 类方法上的装饰器

Python 类方法上的装饰器,python,decorator,class-method,Python,Decorator,Class Method,我的子类继承了父类的一些基本功能。 子类应该有一个泛型构造函数prepare\u和\u connect\u constructor(),它在父类的对象创建过程中发挥了一些魔力。 为了简单起见,这个魔术是由一个简单的基于函数的装饰器完成的(最终,它应该是父类的一部分) 使用这段代码,我终于得到了 obj=super() TypeError:\uuuu init\uuuu()缺少1个必需的位置参数:“a” 运行时,准备和连接构造函数() 实际上,我希望super.\uuuu init\uuuu(a

我的子类继承了父类的一些基本功能。 子类应该有一个泛型构造函数
prepare\u和\u connect\u constructor()
,它在父类的对象创建过程中发挥了一些魔力。 为了简单起见,这个魔术是由一个简单的基于函数的装饰器完成的(最终,它应该是父类的一部分)

使用这段代码,我终于得到了

obj=super()
TypeError:\uuuu init\uuuu()缺少1个必需的位置参数:“a”
运行
时,准备和连接构造函数()

实际上,我希望
super.\uuuu init\uuuu(a)
调用应该与
Child.\uuuu init\uuuu
中的调用相同。 我想原因与
classmethod
有关,但我想不出来

这个电话怎么了


更新:通常错误在于
\uuuu init\uuuu
不返回对象

由于答案中的提示和想法,我修改了代码以实现所需:

class Parent:

    def __init__(self, a):
        self.a = a

    @staticmethod
    def decorate_with_some_magic(func):
        def prepare_and_connect(*args, **kwargs):
            print("prepare something")
            print("create an object")
            obj = func(*args, **kwargs)
            print("connect obj to something")
            return obj

        return prepare_and_connect

    def __repr__(self):
        return f"{self.a}"


class ChildWithOneName(Parent):

    @classmethod
    @Parent.decorate_with_some_magic
    def prepare_and_connect_constructor(cls, a, b):
        """ use the generic connection decorator right on object creation """
        obj = super().__new__(cls)
        obj.__init__(a, b)
        print("Does the same as in it's __init__ method")
        return obj

    def __init__(self, a, b):
        """ init without connecting """
        super().__init__(a)
        self.b = b

    def __repr__(self):
        return f"{self.a}, {self.b}"


class GodChild(Parent):

    @classmethod
    @Parent.decorate_with_some_magic
    def prepare_and_connect_constructor(cls, a, names):
        """ use the generic connection decorator right on object creation """
        obj = super().__new__(cls)
        obj.__init__(a, names)
        # perform some more specific operations
        obj.register_all_names(names)
        print("And does some more stuff than in it's __init__ method")
        return obj

    def __init__(self, a, already_verified_names):
        """ init without connecting """
        super().__init__(a)
        self.verified_names = already_verified_names

    def register_all_names(self, names=[]):
        self.verified_names = []

        def verify(text):
            return True

        for name in names:
            if verify(name):
                self.verified_names.append(name)


    def __repr__(self):
        return f"{self.a}, {self.verified_names}"


if __name__ == '__main__':
    print(ChildWithOneName.prepare_and_connect_constructor("special child", "needs some help"), end='\n\n')
    print(GodChild.prepare_and_connect_constructor("unknown child", "needs some verification"), end='\n\n')
    print(ChildWithOneName("my child", "is clean and doesn't need extra magic"))
  • decoration\u with\u some\u magic
    现在是
    Parent
    类的一部分(使用staticmethod),因为它是相关的通用功能
  • 每个子类(添加一个用于说明)都有自己的
    prepare\u和
    classmethod,它调用自己的构造函数并可选地执行一些附加工作

您对神奇的方法
\uuuuu init\uuuuuuu
\uuuu new\uuuuuuuu
有一点误解<代码>\uuuu new\uuuu
创建新对象,例如返回类的实例<代码>\uuuu init\uuuu只是在适当的位置修改对象。因此,解决您的问题的简单方法是:

@classmethod
@decorate_with_some_magic
def prepare_and_connect_constructor(cls, a, b):
    """ use the generic connection decorator right on object creation """
    obj = super().__new__(cls)
    obj.__init__(a)
    # put some more specific attributes (over the parents class)
    obj.b = b
    return obj

然而,我认为你不应该这样使用它。相反,您可能应该覆盖
\uuuu new\uuu

装饰程序在可调用项上的工作。因为调用函数和初始化类之间没有区别,所以可以直接在类上使用decorator:

def decorate_with_some_magic(func):
    def prepare_and_connect(*args, **kwargs):
        print("prepare something")
        print("create an object")
        obj = func(*args, **kwargs)
        print("connect obj to something")
        return obj

    return prepare_and_connect


class Parent:

    @classmethod
    def prepare_and_connect_constructor(cls, a, b):
        return decorate_with_some_magic(cls)(a, b)

    def __init__(self, a):
        self.a = a

    def __repr__(self):
        return f"{self.a}"

class Child(Parent):

    def __init__(self, a, b):
        """ init without connecting """
        super().__init__(a)
        self.b = b


    def __repr__(self):
        return f"{self.a}, {self.b}"


if __name__ == '__main__':
    normal_child = Child("normal child", "no help needed")
    print(normal_child)
    special_child = Child.prepare_and_connect_constructor("special child", "needs some help")
    print(special_child)
输出:

normal child, no help needed
prepare something
create an object
connect obj to something
special child, needs some help

\uuuuu init\uuuu
不返回任何内容,而且由于您是从类方法调用
super
super()。\uuuuuuu init\uuuuu
需要一个显式实例作为其第一个参数。是的,我没有考虑过这一点。要正确执行此操作,
\uuuu new\uuuuu
的更改应该是什么?@maggie,这会假设您始终想要连接。是这样吗?不是,这就是我为什么要问的原因。它应该是另一种选择(编写更少的代码)。但情况并非总是如此,但我想要一个“普通”构造函数和一个替代构造函数。用这种方法是不可能的。在课堂之外使用装饰器。请看我修改后的答案。好的,这很有效,看起来也不错,但是对于多个子类,这些类的工作方式是不同的。它不能保证在正常构造函数中发生相同的事情。。。也许这不是我想到的最好的设计,但是用类方法做不可能吗?现在用类方法。
normal child, no help needed
prepare something
create an object
connect obj to something
special child, needs some help