Вы неправильно понимаете, что делает super()
. super()
не изменяет тип ссылки self
. super(..., self).method()
все равно будет передавать в этой self
ссылку на вызываемый метод, поэтому self
во всех трех случаях является экземпляром Grandchild()
.
Это означает, что во всех случаях self.aux_func()
следует нормальному порядку разрешения атрибутов, а для Grandchild()
экземпляров, self.aux_func()
всегда найдет Child.aux_func
и вызовет его.
Другими словами, единственный измененный поиск - это атрибут, который вы ищите для самого объекта super()
. Если вам нужно больше таких изменений, вам нужно использовать super()
снова , или , вам нужно дать aux_func()
функциям разные имена для класса . Один из способов сделать это - сделать методы class private .
Последнее можно сделать, назвав вашу функцию двумя подчеркиваниями в начале (но не в конце). Такие имена затем изменяются во время компиляции , чтобы внедрить имена классов во всех местах, на которые они ссылаются:
class Parent:
def __aux_func(self):
# class private to Parent
return "[parent aux]"
def main_func(self):
# any reference to __aux_func *in this class* will use
# the Parent class-private version
print("[parent main]" + self.__aux_func())
class Child(Parent):
def __aux_func(self):
# class private to Child
return "[child aux]"
def main_func(self):
# any reference to __aux_func *in this class* will use
# the Child class-private version
print("[child main]" + self.__aux_func())
См. Зарезервированные классы идентификаторов Документация :
__*
Классно-частные имена. Имена в этой категории, когда они используются в контексте определения класса, переписываются для использования искаженной формы, чтобы избежать столкновения имен между «частными» атрибутами базового и производного классов.
и Идентификаторы (имена) раздел :
Искажение частного имени : Когда идентификатор, который встречается в тексте в определении класса, начинается с двух или более символов подчеркивания и не заканчивается двумя или более символами подчеркивания, он считается частным именем этого класса. Частные имена преобразуются в более длинную форму, прежде чем для них генерируется код. Преобразование вставляет имя класса с удаленными начальными подчеркиваниями и вставленным одиночным подчеркиванием перед именем. Например, идентификатор __spam
в классе с именем Ham
будет преобразован в _Ham__spam
. Это преобразование не зависит от синтаксического контекста, в котором используется идентификатор.
При использовании частного имени класса для __aux_func
любая ссылка на него из методов, определенных в Parent
, будет искать и находить _Parent__aux_func
, а любая ссылка на то же имя в Child
будет искать и находить _Child__aux_func
. Два имени различны и поэтому не конфликтуют:
>>> class Grandchild(Child, Parent):
... @classmethod
... def do_something(cls):
... g = Grandchild()
... g.main_func()
... super(Child, g).main_func()
... Parent.main_func(g)
...
>>> Grandchild.do_something()
[child main][child aux]
[parent main][parent aux]
[parent main][parent aux]
Другой способ добиться этого - явно использовать разные имена; скажем parent_aux_func()
и child_aux_func()
. Частные имена классов на самом деле только предназначены в API, которые предназначены для создания подклассов сторонним кодом без слишком большого ограничения на то, какие имена может использовать подкласс.