基础 class 中的派生范围 class - python 中的继承
Scope of derived class in base class - Inheritance in python
我知道是通过继承基础 class。 base class 中的所有函数也可以在派生 class 中访问。但是它如何以另一种方式工作,这意味着在子 class 中定义的函数可以在基 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())
我知道实例正在通过自己传递,但是子 class 的详细信息在逻辑上应该无法在基 class 中访问,对吗?!
您正在访问一个实例的属性,而不是 class。您的 self
引用永远不是 fish
class 的实例,只是两个派生 classes 之一的实例,而那些派生的 classes 设置 _colour
属性。
如果您创建了 fish()
本身的实例,您会收到属性错误,因为该实例不会设置属性。
你可能会认为在基classes中,self
成为基class的一个实例;事实并非如此。
相反,实例的属性直接在实例上查找,和在其class和基础classes上。因此 self._colour
查看实例,在 type(instance)
和 type(instance).__mro__
中的所有其他对象,方法解析顺序将层次结构中的所有 class 设置为线性顺序.
您可以打印出对象的 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
self
引用是派生的 classes 的实例,传递给继承的方法。所以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>
仔细查看我访问的对象的名称,以及当我在函数上调用 __get__
方法时会发生什么。 Python 当您访问实例的某些属性时,使用一个名为 binding 的过程;当你以这种方式访问一个属性,并指向一个带有 __get__
方法的对象时,该对象被称为 descriptor,并且 __get__
被称为将对象绑定到您查看该对象的任何对象。见 descriptor howto.
在实例上访问color
,产生一个绑定方法对象,但是对象的描述告诉我们它来自fish
,它被命名为实例引用的*绑定方法fish.color
。在 class 上访问相同的名称给我们 fish.color
函数 ,我可以手动绑定它以再次创建方法。
最后,该方法有一个属性__self__
,这是原始实例,__func__
是原始函数。神奇的是,当您调用绑定方法时,方法对象只调用 __func__(__self__, ....)
,因此传入它绑定的实例。
当该函数被继承时,(在 fish
class 上找到,所以 fish.color
),它 仍然传递了派生 class,并且仍然拥有派生的 class 所拥有的一切。
Python 非常动态,非常 灵活。您可以采用任何旧函数并将其放在 class 上,并且可以将其绑定到方法中。或者您可以采用任何未绑定的函数,并手动传入具有正确属性的对象,它会正常工作。 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!'
因此,即使传入一个 class 对象,与 fish
层次结构完全无关,但具有预期的属性,仍然有效。
对于大多数 Python 代码,如果它走路像鸭子,叫起来像鸭子,代码将接受 它是鸭子。称之为 duck typing.
派生 class 的 方法 在基础 class 中不可用。但是, 字段 为在特定对象上运行的任何函数共享。 self._colour
指的是您调用 color()
的对象中 _colour
的值,无论 _colour
是如何设置的。
Edit 因为你直接在 class 中设置 _colour = ...
,在函数之外,任何 catfish
都会有 _colour == "Blue Cat fish"
和任何 tunafish
将有 _colour == "Yellow Tuna fish"
。这些值虽然是在 class 上设置的,但在每个实例中都可用。这就是为什么即使您从未直接说 self._colour = ...
,self._colour
仍然有效。如果你想要 fish-specific 颜色,你需要在 catfish.__init__
或 tunafish.__init__
.
中设置 self._colour
我知道是通过继承基础 class。 base class 中的所有函数也可以在派生 class 中访问。但是它如何以另一种方式工作,这意味着在子 class 中定义的函数可以在基 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())
我知道实例正在通过自己传递,但是子 class 的详细信息在逻辑上应该无法在基 class 中访问,对吗?!
您正在访问一个实例的属性,而不是 class。您的 self
引用永远不是 fish
class 的实例,只是两个派生 classes 之一的实例,而那些派生的 classes 设置 _colour
属性。
如果您创建了 fish()
本身的实例,您会收到属性错误,因为该实例不会设置属性。
你可能会认为在基classes中,self
成为基class的一个实例;事实并非如此。
相反,实例的属性直接在实例上查找,和在其class和基础classes上。因此 self._colour
查看实例,在 type(instance)
和 type(instance).__mro__
中的所有其他对象,方法解析顺序将层次结构中的所有 class 设置为线性顺序.
您可以打印出对象的 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
self
引用是派生的 classes 的实例,传递给继承的方法。所以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>
仔细查看我访问的对象的名称,以及当我在函数上调用 __get__
方法时会发生什么。 Python 当您访问实例的某些属性时,使用一个名为 binding 的过程;当你以这种方式访问一个属性,并指向一个带有 __get__
方法的对象时,该对象被称为 descriptor,并且 __get__
被称为将对象绑定到您查看该对象的任何对象。见 descriptor howto.
在实例上访问color
,产生一个绑定方法对象,但是对象的描述告诉我们它来自fish
,它被命名为实例引用的*绑定方法fish.color
。在 class 上访问相同的名称给我们 fish.color
函数 ,我可以手动绑定它以再次创建方法。
最后,该方法有一个属性__self__
,这是原始实例,__func__
是原始函数。神奇的是,当您调用绑定方法时,方法对象只调用 __func__(__self__, ....)
,因此传入它绑定的实例。
当该函数被继承时,(在 fish
class 上找到,所以 fish.color
),它 仍然传递了派生 class,并且仍然拥有派生的 class 所拥有的一切。
Python 非常动态,非常 灵活。您可以采用任何旧函数并将其放在 class 上,并且可以将其绑定到方法中。或者您可以采用任何未绑定的函数,并手动传入具有正确属性的对象,它会正常工作。 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!'
因此,即使传入一个 class 对象,与 fish
层次结构完全无关,但具有预期的属性,仍然有效。
对于大多数 Python 代码,如果它走路像鸭子,叫起来像鸭子,代码将接受 它是鸭子。称之为 duck typing.
派生 class 的 方法 在基础 class 中不可用。但是, 字段 为在特定对象上运行的任何函数共享。 self._colour
指的是您调用 color()
的对象中 _colour
的值,无论 _colour
是如何设置的。
Edit 因为你直接在 class 中设置 _colour = ...
,在函数之外,任何 catfish
都会有 _colour == "Blue Cat fish"
和任何 tunafish
将有 _colour == "Yellow Tuna fish"
。这些值虽然是在 class 上设置的,但在每个实例中都可用。这就是为什么即使您从未直接说 self._colour = ...
,self._colour
仍然有效。如果你想要 fish-specific 颜色,你需要在 catfish.__init__
或 tunafish.__init__
.
self._colour