简体   繁体   English

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

[英]Scope of derived class in base class - Inheritance in python

I know that by inheriting the base class. 我知道通过继承基类。 All the functions in base class would be accessible in the derived class as well. 基类中的所有功能也可以在派生类中访问。 But how does it work the other way, meaning can a function that is defined in the child class be accessible in the base class. 但是它是如何工作的,这意味着可以在基类中访问子类中定义的函数。

I tried the above out with an example. 我用一个例子尝试了上面。 And it works just fine. 而且效果很好。 But how can that be. 但是那怎么可能。 I am not able to get the logic behind the working. 我无法理解工作背后的逻辑。

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())

I understand that the instance is being passed and all through self, but the details of the child class should logically not be accessible in the base class, correct?! 我知道实例是通过自我传递的,但是从逻辑上讲,子类的详细信息应该不能在基类中访问,对吗?

You are accessing attributes on an instance, not on a class . 您正在访问实例而不是类的属性。 Your self reference is never an instance of the fish class, only of one of the two derived classes, and those derived classes set the _colour attribute. 您的self引用绝不会是fish类的实例,只能是两个派生类之一的实例,而这些派生类将设置_colour属性。

If you created an instance of fish() itself, you'd get an attribute error, because that instance will not have the attribute set. 如果您自己创建了fish()的实例,则会出现属性错误,因为该实例将没有设置属性。

You may perhaps think that in base classes, self becomes an instance of the base class; 您可能会认为,在基类中, self成为基类的一个实例。 that's not the case. 事实并非如此。

Instead, attributes on an instance are looked up on the instance directly, and on its class and base classes. 相反,在实例属性的情况下直接查找, 于它的类和基类。 So self._colour looks at the instance, at type(instance) and at all further objects in the type(instance).__mro__ , the Method Resolution Order that sets all classes in a hierarchy in a linear order. 因此self._colour查看实例, type(instance)以及type(instance)中的所有其他对象type(instance).__mro__ (方法解析顺序),它以线性顺序设置层次结构中的所有类。

You could print out the type() of your object: 您可以打印出对象的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

The self references are instances of the derived classes, passed into an inherited method. self引用是派生类的实例,传递给继承的方法。 So self._colour will first look at the attributes directly set on self , then at type(self) , and there _colour is found. 因此self._colour将首先查看直接在self设置的属性,然后查看type(self) ,然后找到_colour

Perhaps it would help to see how Python methods work. 也许这将有助于了解Python方法的工作方式。 Methods are just thin wrappers around functions, created when you look up the attribute on an instance: 方法只是函数的薄包装,它们是在实例上查找属性时创建的:

>>> 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>

Look closely at the names of the objects I access, and what happens when I call the __get__ method on a function. 仔细查看我访问的对象的名称,以及在函数上调用__get__方法时会发生什么。 Python uses a process called binding when you access certain attributes on an instance; 当您访问实例上的某些属性时,Python使用称为绑定的过程。 when you access an attribute this way, and that points to an object with a __get__ method, then that object is called a descriptor , and __get__ is called to bind the object to whatever you looked the object up on. 当您以这种方式访问​​属性并使用__get__方法指向一个对象时,该对象称为描述符 ,并且__get__将该对象绑定到您在对象上查找的对象。 See the descriptor howto . 参见描述符howto

Accessing color on the instance, produces a bound method object, but the description of the object tells us it came from fish , it's named a *bound method fish.color of instance reference . 访问实例上的color会生成一个绑定方法对象,但是该对象的描述告诉我们它来自fish ,它被称为* instance reference的* bound方法fish.color Accessing the same name on the class gives us the fish.color function , and I can manually bind it to create a method again. 在类上访问相同的名称会给我们fish.color 函数 ,我可以手动将其绑定以再次创建一个方法。

Finally, the method has an attribute __self__ , which is the original instance, and __func__ which is the original function. 最后,该方法具有属性__self__ ,这是原始实例,并且__func__这是原始功能。 And there's the magic, when you call a bound method, the method object just calls __func__(__self__, ....) , so passing in the instance it was bound to. 神奇的是,当您调用绑定方法时,方法对象仅调用__func__(__self__, ....) ,因此传入绑定到的实例。

When that function was inherited, (found on the fish class, so fish.color ), it is still passed an instance of the derived class , and still has everything the derived class has. 继承该函数(在fish类上找到,即fish.color )后,该函数仍将传递派生类的实例 ,并且仍然具有派生类拥有的所有内容。

Python is very dynamic, and very flexible. Python非常动态,而且非常灵活。 You can take any old function and put it on a class, and it can be bound into a method. 您可以采用任何旧函数并将其放在类上,并且可以将其绑定到方法中。 Or you can take any unbound function, and manually pass in an object with the right attributes, and it'll just work . 或者,您可以采用任何未绑定的函数,然后手动传递具有正确属性的对象,它就会正常工作 Python doesn't care, really. 真的,Python不在乎。 So you can pass in a new, indepdent type of object and still have the fish.color function work: 因此,您可以传入一个新的,独立类型的对象,并且仍然可以使用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!'

So even passing in a class object, completely unrelated to the fish hierarchy, but with the expected attribute, still works. 因此,即使传入与fish层次结构完全无关但具有预期属性的类对象,也仍然有效。

To most Python code, if it walks like a duck, and quacks like a duck, the code will accept it as a duck. 对于大多数Python代码而言,如果它走路像鸭子,而嘎嘎叫鸭子,则代码会将其接受为鸭子。 Call it duck typing . 称它为鸭子打字

The methods of a derived class are not available in the base class. 派生类的方法在基类中不可用。 However, the fields are shared for any function operating on a particular object. 但是,这些字段对于在特定对象上运行的任何功能都是共享的。 self._colour refers to the value of _colour in the object on which you are calling color() , regardless of how _colour was set. self._colour指的是您要在其上调用color()的对象中_colour的值,而不管如何设置_colour

Edit because you are setting _colour = ... directly in the class, outside of a function, any catfish will have _colour == "Blue Cat fish" and any tunafish will have _colour == "Yellow Tuna fish" . 编辑因为你设置_colour = ...直接在类,函数外,任何catfish都会有_colour == "Blue Cat fish"和任何tunafish将有_colour == "Yellow Tuna fish" Those values, although set on the class, are available in every instance. 这些值尽管在类上设置,但在每个实例中都可用。 That is why self._colour works even though you never directly said self._colour = ... . 这就是为什么即使您从未直接说过self._colour = ... self._colour起作用的原因。 If you wanted fish-specific colors, you would need to set self._colour in catfish.__init__ or tunafish.__init__ . 如果您想具体鱼的颜色,你就需要设置self._colourcatfish.__init__tunafish.__init__

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

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