繁体   English   中英

python:连续__getitem __()调用继承类?

[英]python: consecutive __getitem__() calls in inherited classes?

我遇到了一个我不明白的问题,这个问题非常复杂,所以我尽力在这里分解。 请看下面的实现。 我的问题是:为什么父类调用其子的__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('Klaus','male','12')运行得很平坦,人们已经可以看到,在父的构造函数中,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)

此修复程序对于此特定方案是唯一的,但它通常也是正确的修复程序; 一般来说,父类的唯一责任是尽量不要不必要地破坏子类,但不能指望他们计划子类创建新的类不变量并尝试提前容纳它们。 子类应该具有父类的完整知识,并且子类的责任是确保它不会破坏父类的任何行为,在这种情况下,通过确保在父类之前定义所有新的必需属性。可能会使用需要它们的方法。

至于为什么它调用子的__getitem__ ,这就是子类化默认是如何工作的(不只是在Python中,而是大多数OO语言)。 来自Child.__init__Father.__init__收到的self仍然是一个Child对象,所以先查找Child __getitem__ 如果没有,你会有一些令人困惑的行为,其中一些关于Child索引调用__getitem__而一些没有,这会使它真的不直观。

如果由于某种原因你绝对必须在Father.__init__只使用Father__getitem__ ,你只需要明确你所使用的方法。 而不是self[gender]self.__getitem__(gender) (两者都通过正常的查找程序),做Father.__getitem__(self, gender)明确调用方法的Father版本(并且还要求你通过self显式,因为您使用的是未绑定的方法而不是绑定的方法)。

另一种方法是定义一个指示实例未完全初始化的属性,并在父初始化程序期间绕过子重载,因此在初始化期间调用父方法:

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类查找。 如果selfChild一个实例,它将使用Child.__getitem__ 您可以将其更改为显式使用Father.__getitem__

self._gender = Father.__getitem__(self, gender)

虽然你的整体设计仍然有点混乱,但它可能会导致更多问题。


不相关但重要:

super(self.__class__, self)

永远不要这样做。 如果你在任何获得子类的类中执行此操作,这将会中断。 您必须明确提供类名。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM