Вы обращаетесь к атрибутам экземпляра, а не класса. Ваша ссылка 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
_colour
является атрибутом класса, но к нему осуществляется доступ через экземпляр, поэтому он работает. - person Aran-Fey   schedule 24.03.2018self
никогда не являетсяfish
классом или экземпляром классаfish
. Это экземплярcatfish
или tunafish`, каждый из которых имеет обязательный атрибут. - person Martijn Pieters   schedule 24.03.2018