Да, это работает так, как вы хотите.
Вы можете легко проверить это самостоятельно. Если вы не передадите ничего кроме 0 и 1, должно быть совершенно очевидно, будут ли они в квадрате или в кубах.
И, в случаях, когда это менее очевидно, просто добавьте точку останова отладчика к Child.method1
и Parent.method1
и посмотрите, какая из них получает удар. Или добавьте print(f'Child1.method({self}, {num})')
к методу и посмотрите, распечатывается ли он.
Если вы переходите с другого языка с семантикой ООС C ++ вместо семантики ОО Smalltalk, это может помочь думать об этом так: каждый метод всегда виртуален.
- Являются ли
__init__
звонки виртуальными? Да.
- Что если вы вызовете метод во время
__init__
? Да.
- Что если вы вызовете метод внутри
super
вызова? Да.
- А как насчет
@classmethod
? Да.
- Что если ...? Да.
Единственными исключениями являются случаи, когда вы стараетесь явно указать Python , а не , чтобы вызвать виртуальную функцию:
- Вызов
super()
использует реализацию из следующего класса в цепочке MRO, потому что в этом весь смысл super
.
- Если вы захватите связанный метод родителя и вызовете его, как
Parent.method1(self, num)
, вы, очевидно, получите Parent.method1
, потому что в этом весь смысл связанных методов.
- Если вы покопаетесь в диктатах класса и запустите протокол дескриптора вручную, вы, очевидно, получите все, что делаете вручную.
Если вы не пытаетесь понять Python с точки зрения Java и просто хотите глубже понять Python на его собственных терминах, вам нужно понять, что происходит, когда вы вызываете self.method1(i)
.
Во-первых, self.method1
не знает или не заботится, что вы собираетесь это назвать Это поиск атрибутов, как, скажем, self.name
будет.
Способ, которым Python разрешает это, описан в Descriptor HOWTO , но упрощенная версия выглядит следующим образом:
- Имеет ли
self.__dict__
что-нибудь с именем method1
? Нет.
- Имеет ли
type(self).__dict__
что-нибудь с именем method1
? Да.
- Возврат
type(self).__dict__['method1'].__get__(self)
.
Если этот второй поиск не удался, Python перебрал бы type(self).mro()
и выполнил бы один и тот же тест для каждого. Но здесь это не подходит. type(self)
всегда будет Child
для экземпляра Child
, и Child.__dict__['method1']
существует, поэтому он связывает Child.method
с self
, и в результате получается self.method1
.