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 传递,但是子类的详细信息在逻辑上不应在基类中访问,对吗?!
您正在访问实例上的属性,而不是类上的属性。您的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代码来说,如果它像鸭子一样行走,像鸭子一样嘎嘎叫,那么代码就会将其视为鸭子。称之为鸭子打字。