Область производного класса в базовом классе - наследование в Python

Я знаю это, унаследовав базовый класс. Все функции базового класса также будут доступны в производном классе. Но как это работает по-другому, то есть может ли функция, определенная в дочернем классе, быть доступной в базовом классе.

Я попробовал это на примере. И это прекрасно работает. Но как это может быть. Я не могу понять логику работы.

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

Я понимаю, что экземпляр передается через себя, но детали дочернего класса логически не должны быть доступны в базовом классе, верно ?!


person Abhishek    schedule 24.03.2018    source источник
comment
Нет, методы производного класса недоступны в базе.   -  person Martijn Pieters    schedule 24.03.2018
comment
Результатом вышеупомянутой программы является: рыба Blue Cat Fish Желтый тунец. (Так что это доказывает обратное)   -  person Abhishek    schedule 24.03.2018
comment
Чтобы прояснить комментарий Martijn: _colour является атрибутом класса, но к нему осуществляется доступ через экземпляр, поэтому он работает.   -  person Aran-Fey    schedule 24.03.2018
comment
Да, извините, этот комментарий не помог. self никогда не является fish классом или экземпляром класса fish. Это экземпляр catfish или tunafish`, каждый из которых имеет обязательный атрибут.   -  person Martijn Pieters    schedule 24.03.2018


Ответы (2)


Вы обращаетесь к атрибутам экземпляра, а не класса. Ваша ссылка 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

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>

Внимательно посмотрите на имена объектов, к которым я обращаюсь, и на то, что происходит, когда я вызываю метод __get__ для функции. Python использует процесс, называемый привязкой, когда вы обращаетесь к определенным атрибутам в экземпляре; когда вы обращаетесь к атрибуту таким образом, и это указывает на объект с помощью метода __get__, тогда этот объект называется дескриптором, а __get__ вызывается для привязки объекта к тому, на чем вы искали объект . См. руководство по дескриптору.

Доступ к color в экземпляре создает объект связанный метод, но описание объекта говорит нам, что он пришел из fish, он называется * связанным методом fish.color из ссылки на экземпляр. Доступ к тому же имени в классе дает нам 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!'

Таким образом, даже передача объекта класса, совершенно не связанного с иерархией fish, но с ожидаемым атрибутом, по-прежнему работает.

Для большей части кода Python, если он ходит как утка и крякает как утка, код принимает его как утку. Назовите это утиным вводом.

person Martijn Pieters    schedule 24.03.2018

Методы производного класса недоступны в базовом классе. Однако поля являются общими для любой функции, работающей с конкретным объектом. self._colour относится к значению _colour в объекте, для которого вы вызываете color(), независимо от того, как было установлено _colour.

Изменить, поскольку вы устанавливаете _colour = ... непосредственно в классе, вне функции, любой catfish будет иметь _colour == "Blue Cat fish", а любой tunafish будет иметь _colour == "Yellow Tuna fish". Эти значения, хотя и установлены в классе, доступны в каждом экземпляре. Вот почему self._colour работает, даже если вы никогда прямо не говорили self._colour = .... Если вам нужны цвета, характерные для рыб, вам нужно установить self._colour в catfish.__init__ или tunafish.__init__.

person cxw    schedule 24.03.2018