我遇到了一个我不明白的问题,而且它非常复杂,所以我尽力在这里分解。请看下面的实现。我的问题是:为什么父类调用其子的__getitem__方法,而不是调用它自己的__getitem __?
class Father(object):
''' Father class '''
def __init__(self,name,gender):
print('call __init__ Father')
self._name = name
# trying to call Father's __getitem__ here
self._gender=self[gender]
def __getitem__(self,key):
print('call __getitem__ Father')
return key
class Child(Father):
''' inherited from Father class '''
def __init__(self, name, gender, age=None):
print('call __init__ Child')
super(self.__class__, self).__init__(name, gender)
self._age = age
def __getitem__(self, key):
print('call __getitem__ Child')
if self._name == 'Marie' and self._age == None:
print('I am not sure, how old I am')
return super(self.__class__, self).__getitem__(key)
one=Child('Klaus','male','12')
other=Child('Marie','female')
将出现的错误消息是:
call __init__ Child
call __init__ Father
call __getitem__ Child
call __getitem__ Father
call __init__ Child
call __init__ Father
call __getitem__ Child
Traceback (most recent call last):
File "<ipython-input-58-2d97b09f6e25>", line 1, in <module>
runfile('F:/python-workspace/more-fancy-getitem-fix/test.py', wdir='F:/python-workspace/more-fancy-getitem-fix')
File "C:\Python27\lib\site-packages\spyderlib\widgets\externalshell\sitecustomize.py", line 601, in runfile
execfile(filename, namespace)
File "C:\Python27\lib\site-packages\spyderlib\widgets\externalshell\sitecustomize.py", line 66, in execfile
exec(compile(scripttext, filename, 'exec'), glob, loc)
File "F:/python-workspace/more-fancy-getitem-fix/test.py", line 26, in <module>
other=Child('Marie','female')
File "F:/python-workspace/more-fancy-getitem-fix/test.py", line 16, in __init__
super(self.__class__, self).__init__(name, gender)
File "F:/python-workspace/more-fancy-getitem-fix/test.py", line 6, in __init__
self._gender=self[gender]
File "F:/python-workspace/more-fancy-getitem-fix/test.py", line 21, in __getitem__
if self._name == 'Marie' and self._age == None:
AttributeError: 'Child' object has no attribute '_age'
我不指望这种行为。我试图从父级继承Child添加功能,因此所有上述语法都是必要的,并且在我更复杂的代码中有意义。 第一个例子是一个= Child(&#39; Klaus&#39;,&#39; male&#39;&#39; 12&#39;)运行得很顺利,人们已经可以看到,在父亲的情况下s构造函数调用了Child的__getitem__。 在other = Child(&#39; Marie&#39;,&#39; female&#39;)的第二个例子中,人们可以看到为什么这个反向调用会让我烦恼。在定义self._age = age之前,代码不会运行。
来自于反之亦然调用可能有用的建议以及它的用途,我将非常感谢解决方案如何在父中明确调用自己的__getitem__方法。只是写
self._gender=self.__getitem__(gender) #instead of
self._gender=self[gender]
很遗憾没有做到这一点并产生同样的错误。
答案 0 :(得分:2)
要修复,只需在Child
__init__
中交换初始化顺序,以便Child
所需的Child.__getitem__
属性在需要之前进行初始化:
def __init__(self, name, gender, age=None):
print('call __init__ Child')
self._age = age
super(Child, self).__init__(name, gender)
此修复程序对于此特定方案是唯一的,但它通常也是正确的修复程序;一般来说,父类的唯一责任是尽量不要不必要地破坏子类,但不能指望他们计划子类创建新的类不变量并尝试提前容纳它们。子类应该具有父类的完整知识,并且子类的责任是确保它不会破坏父类的任何行为,在这种情况下,通过确保在父类之前定义所有新的必需属性。可能会使用需要它们的方法。
至于为什么它会调用孩子的__getitem__
,这就是默认情况下子类化的工作方式(不仅仅是Python,而是大多数OO语言)。来自self
的{{1}}收到的Father.__init__
仍然是Child.__init__
对象,因此首先在Child
上查找__getitem__
。如果没有,那么你会有一些令人困惑的行为,Child
上的某些索引会调用Child
,有些则没有,这会让它变得非常直观。
如果出于某种原因,您绝对必须在__getitem__
中仅使用Father
的{{1}},您只需要明确说明您调用的方法。不是__getitem__
或Father.__init__
(两者都通过正常的查找过程),而是self[gender]
明确调用方法的self.__getitem__(gender)
版本(并且还要求您通过显式Father.__getitem__(self, gender)
,因为您使用的是未绑定方法而不是绑定方法。
另一种方法是定义一个属性,指示实例未完全初始化,并在父初始化程序期间绕过子重载,因此在初始化期间调用父方法:
Father
答案 1 :(得分:1)
# trying to call Father's __getitem__ here
self._gender=self[gender]
此处使用的__getitem__
方法将通过实际的self
类进行查找。如果self
是Child
的实例,则会使用Child.__getitem__
。您可以将其更改为明确使用Father.__getitem__
:
self._gender = Father.__getitem__(self, gender)
虽然你的整体设计仍然很乱,但它可能会导致更多的问题。
无关但重要:
super(self.__class__, self)
永远不要这样做。如果您在任何已经获得子类的类中执行此操作,这将会中断。您必须明确提供类名。