继承具有子类MR的别名

2024-04-26 08:09:32 发布

您现在位置:Python中文网/ 问答频道 /正文

class parent:
    def fun1(self):
        print("foo")

    fun2 = fun1

class child(parent):
    def fun1(self):
        print("bar")

child().fun2()

此代码输出“foo”。我很确定我理解为什么会发生这种情况,但我想知道是否有一种简单的方法来实现我想要的(可继承的别名根据MRO的行为,所以它会输出“bar”),或者这是一种糟糕的编程实践的原因。你知道吗


Tags: 方法代码selfchildfoodef编程bar
3条回答

可以将fun2作为返回self.fun1的属性

class Parent:
    def fun1(self):
        print('foo')
    @property
    def fun2(self):
        return self.fun1

class Child(Parent):
    def fun1(self):
        print('bar')

Child().fun2()
# bar

没有办法直接做你想做的事。你知道吗

要了解原因,您需要了解方法查找的工作方式。它在the Descriptor HOWTO中有详细的解释,我试着写一个不太专业的解释here,所以我假设你读了这两个,并且只显示效果:

>>> child.fun1
<function __main__.child.fun1>
>>> child.fun2
<function __main__.parent.fun1>
>>> child().fun2
<bound method parent.fun1 of <__main__.child object at 0x10b735ba8>>

注意,child.fun2parent.fun1,而不是child.fun1。这解释了为什么child().fun2最终成为parent.fun1周围的绑定方法,即使self最终成为child实例。为什么?很明显,fun2不在child.__dict__(或者我们不需要MRO)。在parent.__dict__中,它只能是parent.fun1。你知道吗

那么,解决办法是什么?你知道吗

你可以把fun2变成一个property转发给fun2,就像Patrick Haugh建议的那样。或者你可以这样做:

def fun2(self):
    return self.fun1()

这个解决方案的好处是非常简单,任何懂Python的人都会理解它的工作原理。你知道吗

但是Patrick的解决方案有一个优点,那就是不仅使child().fun2(),而且使child().fun2(作为一个可以传递、比较身份等的对象)按您希望的方式工作。你知道吗


同时,还有一个与您所要求的内容密切相关的习惯用法,您不希望覆盖的一组公共方法调用您所做的“受保护”实现方法。例如,一个简单的1D数组数学类可能会执行以下操作:

def _math(lhs, rhs, op):
    # not actually a useful implementation...
    return [op(x, y) for x, y in zip(lhs, rhs)] 
def __add__(self, other):
    return self._math(other, add)
def __radd__(self, other):
    return self._math(other, add)
# etc.

现在在__add____radd__之间没有不对称,只有在“公共”接口(__add____radd__)和“受保护”接口(_math)之间。你知道吗

这里有一个非常简单的方法,完全可以:

class Parent:
    def fun1(self):
        print("foo")

    def fun2(self):
        return self.fun1()


class Child(Parent):
    def fun1(self):
        print("bar")


Child().fun2()  # -> bar

相关问题 更多 >