基类中派生类的范围-python中的继承

Abh*_*hek 5 python inheritance base-class

我知道通过继承基类。基类中的所有函数也可以在派生类中访问。但是它是如何以另一种方式工作的,这意味着在子类中定义的函数可以在基类中访问。

我用一个例子尝试了上面的内容。它工作得很好。但这怎么可能。我无法理解工作背后的逻辑。

class fish:

    def color(self):
        # _colour is a property of the child class. How can base class access this?
        return self._colour  


class catfish(fish):

    _colour = "Blue Cat fish"

    def speed(self):
        return "Around 100mph"

    def agility(self):
        return "Low on the agility"

class tunafish(fish):

    _colour = "Yellow Tuna fish"

    def speed(self):
        return "Around 290mph"

    def agility(self):
        return "High on the agility"

catfish_obj = catfish()
tunafish_obj = tunafish()

print(catfish_obj.color())
print(tunafish_obj.color())
Run Code Online (Sandbox Code Playgroud)

我知道实例正在传递并且全部通过 self 传递,但是子类的详细信息在逻辑上不应在基类中访问,对吗?!

Mar*_*ers 4

您正在访问实例上的属性,而不是类上的属性。您的self引用永远不是该类的实例fish,而只是两个派生类之一的实例,并且这些派生类设置该_colour属性。

如果您创建了自身的实例fish(),则会收到属性错误,因为该实例不会设置属性。

您可能会认为在基类中,self成为基类的实例;事实并非如此。

相反,直接在实例及其类和基类上查找实例的属性。因此,self._colour查看实例以及type(instance)中的所有其他对象type(instance).__mro__,方法解析顺序以线性顺序设置层次结构中的所有类。

您可以打印出type()您的对象的:

>>> class fish:
...     def color(self):
...         print(type(self))
...         return self._colour
...
# your other class definitions
>>> print(catfish_obj.color())
<class '__main__.catfish'>
Blue Cat fish
>>> print(tunafish_obj.color())
<class '__main__.tunafish'>
Yellow Tuna fish
Run Code Online (Sandbox Code Playgroud)

引用self是派生类的实例,传递到继承的方法中。所以self._colour会先看直接在 上设置的属性self,然后在 上type(self)_colour找到了。

也许了解 Python 方法的工作原理会有所帮助。方法只是函数的薄包装,当您在实例上查找属性时创建:

>>> tunafish_obj.color  # access the method but not calling it
<bound method fish.color of <__main__.tunafish object at 0x110ba5278>>
>>> tunafish.color      # same attribute name, but on the class
<function fish.color at 0x110ba3510>
>>> tunafish.color.__get__(tunafish_obj, tunafish)  # what tunafish_obj.color actually does
<bound method fish.color of <__main__.tunafish object at 0x110ba5278>>
>>> tunafish_obj.color.__self__   # methods have attributes like __self__
<__main__.tunafish object at 0x110ba5278>
>>> tunafish_obj.color.__func__   # and __func__. Recognise the numbers?
<function fish.color at 0x110ba3510>
Run Code Online (Sandbox Code Playgroud)

仔细查看我访问的对象的名称,以及当我调用__get__函数的方法时会发生什么。当您访问实例上的某些属性时, Python 使用称为绑定的过程;当您以这种方式访问​​属性并且该属性指向带有__get__方法的对象时,该对象称为描述符,并被__get__调用以将该对象绑定到您查找该对象的任何内容。请参阅描述符 howto

访问color实例,会产生一个绑定方法对象,但该对象的描述告诉我们它来自fish,它被命名为实例引用fish.color的*绑定方法。访问类上的相同名称为我们提供了function,我可以手动绑定它以再次创建方法。fish.color

最后,该方法有一个属性__self__,它是原始实例,__func__也是原始函数。神奇的是,当您调用绑定方法时,方法对象只调用__func__(__self__, ....),因此传入它所绑定的实例。

当该函数被继承时(在fish类上找到,所以fish.color),它仍然传递派生类的实例,并且仍然拥有派生类所拥有的一切。

Python 非常动态且非常灵活。您可以采用任何旧函数并将其放在类中,并且可以将其绑定到方法中。或者,您可以采用任何未绑定的函数,并手动传入具有正确属性的对象,它就会正常工作。Python 并不关心,真的。因此,您可以传入一个新的、独立类型的对象,并且该fish.color函数仍然可以工作:

>>> fish.color  # original, unbound function on the base class
<function fish.color at 0x110ba3510>
>>> class FakeFish:
...     _colour = 'Fake!'
...
>>> fish.color(FakeFish)  # passing in a class! Uh-oh?
<class 'type'>
'Fake!'
Run Code Online (Sandbox Code Playgroud)

因此,即使传递一个与层次结构完全无关fish但具有预期属性的类对象,仍然有效。

对于大多数Python代码来说,如果它像鸭子一样行走,像鸭子一样嘎嘎叫,那么代码就会将其视为鸭子。称之为鸭子打字