super().with_two_legs('Human')
事实上
Animal
的
with_two_legs
,但它通过了
Human
作为
cls
,不是
动物
.
super()
使代理对象仅用于帮助方法查找,它不会更改传递的内容(它仍然是相同的
self
或
cls公司
它起源于)。在这种情况下,
超级()
什么都没用,因为
人
不覆盖
有两条腿
,所以:
super().with_two_legs('Human')
意思是“呼叫
有两条腿
一等以上
人
在定义它的层次结构中”,以及:
cls.with_two_legs('Human')
意思是“呼叫
有两条腿
在层次结构中的第一个类上
cls公司
这就是它的定义”。只要下面没有课
动物
定义它,它们做同样的事情。
这意味着你的代码在
return cls(name_full, 2)
,因为
cls公司
仍然
人
,以及您的
Human.__init__
不带任何争论
自己
. 即使你想让它起作用(例如添加两个你忽略的可选参数),这也会导致无限循环,如
人类__
打电话
Animal.with_two_legs
,它反过来试图构造
人
,呼叫
人类__
再一次。
您要做的并不是一个好主意;根据其性质,备用构造函数依赖于类的核心构造函数/初始值设定项。如果尝试生成依赖于备用构造函数的核心构造函数/初始值设定项,则创建了循环依赖项。
在这种情况下,我建议避免使用备用构造函数,而是显式地提供
legs
始终计数,或使用中间值
TwoLeggedAnimal
类执行备用构造函数的任务。如果你想重用代码,第二个选项就意味着你的“从名称生成完整名称的非常长的代码”可以进入
双腿动物
的
__init__
;在第一个选项中,您只需编写
staticmethod
这就排除了代码的因素,所以这两种代码都可以使用
有两条腿
以及其他需要使用它的构造函数。
类层次结构类似于:
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)
旁注:即使你处理递归,你所要做的也不会起作用,因为
__初始__
不
制作
新实例,它初始化现有实例。所以即使你打电话
super()。有两条腿(“人类”)
它以某种方式工作,它生成并返回一个完全不同的实例,但对
自己
接收人
__初始__
这就是真正被创造出来的东西。你能做的最好的事情是:
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))
无法在中调用备用构造函数
__初始__
换个衣服
自己
含蓄地。我能想到的唯一办法是,在不创建helper方法和保留备用构造函数的情况下,使用
__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