我想执行以下操作(在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')
实际上确实调用Animal
的with_two_legs
,但它Human
作为传递cls
,而不是Animal
。super()
使代理对象仅用于辅助方法查找,它不会更改传递的内容(它仍然相同self
或cls
源自)。在这种情况下,super()
甚至没有做任何有用的事情,因为Human
它不会覆盖with_two_legs
,所以:
super().with_two_legs('Human')
表示“with_two_legs
从Human
定义它的层次结构中的上述第一类发出的呼叫”,并且:
cls.with_two_legs('Human')
表示“with_two_legs
从cls
定义它的层次结构中调用第一个类”。只要下面没有类Animal
定义它,它们就可以做同样的事情。
这意味着您的代码在处中断return cls(name_full, 2)
,因为cls
它仍然存在Human
,并且您Human.__init__
不会接受任何参数self
。即使您四处寻找使它起作用的方法(例如,通过添加两个忽略的可选参数),也将导致无限循环(Human.__init__
称为)Animal.with_two_legs
,进而试图构造一个Human
,Human.__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] 删除。
我来说两句