python:在继承 类 中连续调用 __getitem__()?
python: consecutive __getitem__() calls in inherited classes?
我遇到了一个我不明白的问题,而且它很复杂,所以我尽力在这里分解它。请看下面的实现。我的问题是:为什么父class调用它child的__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 添加功能,因此上述所有语法都是必要的,并且在我更复杂的代码中有意义。
one=Child('Klaus','male','12') 运行s 的第一个例子很顺利,我们已经可以看到,在父亲的构造函数中 Child 的 __getitem__ 被调用。
在 other=Child('Marie','female') 的第二个例子中,可以看出为什么这个反向调用让我烦恼。这里的代码不会 运行 直到 self._age = age 被定义。
除了建议这个反之亦然的调用可能如何有用以及它的目的,我将非常感谢解决如何在父亲中显式调用自己的 __getitem__ 方法的解决方案。刚写
self._gender=self.__getitem__(gender) #instead of
self._gender=self[gender]
不幸的是没有成功并产生同样的错误。
要修复,只需交换 Child
__init__
中的初始化顺序,以便 Child.__getitem__
所需的 Child
属性在需要之前被初始化:
def __init__(self, name, gender, age=None):
print('call __init__ Child')
self._age = age
super(Child, self).__init__(name, gender)
这个修复是这个特定场景所独有的,但它通常也是正确的修复;一般来说,parent classes 的唯一责任是尽量不要不必要地破坏 subclassing,但不能指望他们计划 child class 创建新的 class 不变量并尝试提前容纳它们。 child class 应该完全了解 parent,child class 有责任确保它不会破坏任何行为在 parent 中,在这种情况下,通过确保在 parent 可能使用需要它们的方法之前定义所有新的必需属性。
至于为什么它调用 child 的 __getitem__
,这就是 subclassing 默认工作的方式(不仅在 Python 中,而且大多数 OO语言)。 Father.__init__
从 Child.__init__
收到的 self
仍然是 Child
object,因此查找首先检查 Child
上的 __getitem__
。如果没有,您会出现令人困惑的行为,其中 Child
上的一些索引调用了 __getitem__
而有些则没有,这会使它真正成为 non-intuitive.
如果出于某种原因您绝对必须在 Father.__init__
中仅使用 Father
的 __getitem__
,您只需要明确说明您调用的方法。而不是 self[gender]
或 self.__getitem__(gender)
(两者都通过正常的查找过程),执行 Father.__getitem__(self, gender)
显式调用方法的 Father
版本(并且还要求您通过self
明确地,因为您使用的是未绑定方法而不是绑定方法。
另一种方法是定义一个指示实例未完全初始化的属性,并在 parent 初始化程序期间绕过 child 重载,因此在初始化期间调用 parent 方法:
def __init__(self, name, gender, age=None):
print('call __init__ Child')
# Use double underscore prefix to make a private attribute of Child
# not directly visible to parent or lower level children so no risk
# of being reused by accident
self.__in_parent_init = True
super(Child, self).__init__(name, gender)
self.__in_parent_init = False
self._age = age
def __getitem__(self, key):
if self.__in_parent_init:
# Don't use Child specific features until Father initialized
return super(Child, self).__getitem__(key)
print('call __getitem__ Child')
if self._name == 'Marie' and self._age == None:
print('I am not sure, how old I am')
return super(Child, self).__getitem__(key)
# trying to call Father's __getitem__ here
self._gender=self[gender]
这里使用的__getitem__
方法要通过self
的实际class来查找。如果 self
是 Child
的实例,它将使用 Child.__getitem__
。您可以将其更改为显式使用 Father.__getitem__
:
self._gender = Father.__getitem__(self, gender)
虽然你的整体设计还是有点乱,而且很可能会导致更多的问题。
无关但重要:
super(self.__class__, self)
永远不要这样做。如果你在任何被子class编辑的class中这样做,这将会中断。您必须明确提供 class 名称。
我遇到了一个我不明白的问题,而且它很复杂,所以我尽力在这里分解它。请看下面的实现。我的问题是:为什么父class调用它child的__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 添加功能,因此上述所有语法都是必要的,并且在我更复杂的代码中有意义。 one=Child('Klaus','male','12') 运行s 的第一个例子很顺利,我们已经可以看到,在父亲的构造函数中 Child 的 __getitem__ 被调用。 在 other=Child('Marie','female') 的第二个例子中,可以看出为什么这个反向调用让我烦恼。这里的代码不会 运行 直到 self._age = age 被定义。
除了建议这个反之亦然的调用可能如何有用以及它的目的,我将非常感谢解决如何在父亲中显式调用自己的 __getitem__ 方法的解决方案。刚写
self._gender=self.__getitem__(gender) #instead of
self._gender=self[gender]
不幸的是没有成功并产生同样的错误。
要修复,只需交换 Child
__init__
中的初始化顺序,以便 Child.__getitem__
所需的 Child
属性在需要之前被初始化:
def __init__(self, name, gender, age=None):
print('call __init__ Child')
self._age = age
super(Child, self).__init__(name, gender)
这个修复是这个特定场景所独有的,但它通常也是正确的修复;一般来说,parent classes 的唯一责任是尽量不要不必要地破坏 subclassing,但不能指望他们计划 child class 创建新的 class 不变量并尝试提前容纳它们。 child class 应该完全了解 parent,child class 有责任确保它不会破坏任何行为在 parent 中,在这种情况下,通过确保在 parent 可能使用需要它们的方法之前定义所有新的必需属性。
至于为什么它调用 child 的 __getitem__
,这就是 subclassing 默认工作的方式(不仅在 Python 中,而且大多数 OO语言)。 Father.__init__
从 Child.__init__
收到的 self
仍然是 Child
object,因此查找首先检查 Child
上的 __getitem__
。如果没有,您会出现令人困惑的行为,其中 Child
上的一些索引调用了 __getitem__
而有些则没有,这会使它真正成为 non-intuitive.
如果出于某种原因您绝对必须在 Father.__init__
中仅使用 Father
的 __getitem__
,您只需要明确说明您调用的方法。而不是 self[gender]
或 self.__getitem__(gender)
(两者都通过正常的查找过程),执行 Father.__getitem__(self, gender)
显式调用方法的 Father
版本(并且还要求您通过self
明确地,因为您使用的是未绑定方法而不是绑定方法。
另一种方法是定义一个指示实例未完全初始化的属性,并在 parent 初始化程序期间绕过 child 重载,因此在初始化期间调用 parent 方法:
def __init__(self, name, gender, age=None):
print('call __init__ Child')
# Use double underscore prefix to make a private attribute of Child
# not directly visible to parent or lower level children so no risk
# of being reused by accident
self.__in_parent_init = True
super(Child, self).__init__(name, gender)
self.__in_parent_init = False
self._age = age
def __getitem__(self, key):
if self.__in_parent_init:
# Don't use Child specific features until Father initialized
return super(Child, self).__getitem__(key)
print('call __getitem__ Child')
if self._name == 'Marie' and self._age == None:
print('I am not sure, how old I am')
return super(Child, self).__getitem__(key)
# trying to call Father's __getitem__ here
self._gender=self[gender]
这里使用的__getitem__
方法要通过self
的实际class来查找。如果 self
是 Child
的实例,它将使用 Child.__getitem__
。您可以将其更改为显式使用 Father.__getitem__
:
self._gender = Father.__getitem__(self, gender)
虽然你的整体设计还是有点乱,而且很可能会导致更多的问题。
无关但重要:
super(self.__class__, self)
永远不要这样做。如果你在任何被子class编辑的class中这样做,这将会中断。您必须明确提供 class 名称。