用python中的父类方法重写__init__

伊格纳西奥

我想执行以下操作(在Python 3.7中):

class Animal:

    def __init__(self, name, legs):
        self.legs = legs
        print(name)

    @classmethod 
    def with_two_legs(cls, name):
        # extremely long code to generate name_full from name
        name_full = name
        return cls(name_full, 2)

class Human(Animal):

    def __init__(self):
        super().with_two_legs('Human')

john = Human()

基本上,我想__init__用父类的工厂类方法覆盖子类方法。但是,编写的代码不起作用,并引发:

TypeError: __init__() takes 1 positional argument but 3 were given

我认为这意味着super().with_two_legs('Human')Human作为cls变量传递

1)为什么这不能按书面规定进行?我认为super()将返回超的代理实例,所以cls会是Animal正确的?

2)即使是这种情况,我也不认为这段代码可以实现我想要的结果,因为classmethod返回的实例Animal,但是我只想以Human与classmethod相同的方式进行初始化,有没有办法实现我的行为想?

我希望这不是一个很明显的问题,我发现文件super()有些混乱。

暗影游侠

super().with_two_legs('Human')实际上确实调用Animalwith_two_legs,但它Human作为传递cls,而不是Animalsuper()使代理对象仅用于辅助方法查找,它不会更改传递的内容(它仍然相同selfcls源自)。在这种情况下,super()甚至没有做任何有用的事情,因为Human它不会覆盖with_two_legs,所以:

super().with_two_legs('Human')

表示“with_two_legsHuman定义它的层次结构中的上述第一类发出的呼叫”,并且:

cls.with_two_legs('Human')

表示“with_two_legscls定义它的层次结构中调用第一个类”。只要下面没有类Animal定义它,它们就可以做同样的事情。

这意味着您的代码在处中断return cls(name_full, 2),因为cls它仍然存在Human,并且您Human.__init__不会接受任何参数self即使您四处寻找使它起作用的方法(例如,通过添加两个忽略的可选参数),也将导致无限循环(Human.__init__称为)Animal.with_two_legs,进而试图构造一个HumanHuman.__init__再次调用

您要尝试做的并不是一个好主意;备用构造函数,就其性质而言,取决于类的核心构造函数/初始化程序。如果尝试创建依赖于备用构造函数的核心构造函数/初始化程序,则已创建了循环依赖项。

在这种特殊情况下,我建议避免使用替代构造函数,而希望显式地legs始终提供计数,或者使用中间TwoLeggedAnimal类来执行替代构造函数的任务。如果您想重用代码,第二个选项仅表示您的“从名称生成name_full的超长代码”可以放在TwoLeggedAnimal的中__init__在第一个选项中,您只需编写一个staticmethod将代码排除在外的代码,以便供需要使用该代码的with_two_legs其他构造函数使用。

类层次结构如下所示:

class Animal:
    def __init__(self, name, legs):
        self.legs = legs
        print(name)

class TwoLeggedAnimal(Animal)
    def __init__(self, name):
        # extremely long code to generate name_full from name
        name_full = name
        super().__init__(name_full, 2)

class Human(TwoLeggedAnimal):
    def __init__(self):
        super().__init__('Human')

通用代码方法将改为:

class Animal:
    def __init__(self, name, legs):
        self.legs = legs
        print(name)

    @staticmethod
    def _make_two_legged_name(basename):
        # extremely long code to generate name_full from name
        return name_full

    @classmethod 
    def with_two_legs(cls, name):
        return cls(cls._make_two_legged_name(name), 2)

class Human(Animal):
    def __init__(self):
        super().__init__(self._make_two_legged_name('Human'), 2)

旁注:即使您绕过递归,您尝试执行的操作也不起作用,因为__init__它不会创建新实例,而是会初始化现有实例。因此,即使您调用super().with_two_legs('Human')它并以某种方式起作用,它也会创建并返回一个完全不同的实例,但self对接收到的东西不做任何事情,而接收到的__init__东西实际上是在创建的。您可能要做的最好的事情是:

def __init__(self):
    self_template = super().with_two_legs('Human')
    # Cheaty way to copy all attributes from self_template to self, assuming no use
    # of __slots__
    vars(self).update(vars(self_template))

没有办法在其中调用备用构造函数__init__并使它self隐式更改我可以想到的以这种方式实现您的预​​期目的而不创建辅助方法并保留备用构造函数的唯一方法是使用__new__代替__init__(这样您就可以返回由另一个构造函数创建的实例),并使用备用构造函数显式调用顶级类,__new__以避免循环调用依赖项:

class Animal:
    def __new__(cls, name, legs):  # Use __new__ instead of __init__
        self = super().__new__(cls)  # Constructs base object
        self.legs = legs
        print(name)
        return self  # Returns initialized object
    @classmethod
    def with_two_legs(cls, name):
        # extremely long code to generate name_full from name
        name_full = name
        return Animal.__new__(cls, name_full, 2)  # Explicitly call Animal's __new__ using correct subclass

class Human(Animal):
    def __new__(cls):
        return super().with_two_legs('Human')  # Return result of alternate constructor

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章