Python, использующий super с внуком, наследующим родителей и детей - PullRequest
0 голосов
/ 28 августа 2018

[используя Python3.6] У меня есть дизайн, где внук наследует родителей и детей (от родителей).

class Parent:
    def aux_func(self):
        return "[parent aux]"

    def main_func(self):
        print("[parent main]" + self.aux_func())


class Child(Parent):
    def aux_func(self):
        return "[child aux]"

    def main_func(self):
        print("[child main]" + self.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][child aux]
[parent main][child aux]

Вызов функции из Parent приводит к разрешению aux_func из класса Child. Я пытался пройти через процесс MRO, но не могу объяснить функции, вызываемые из разных классов. Может ли кто-нибудь помочь мне с

  1. Почему это происходит?
  2. Каким будет обходной путь для достижения [parent main] [parent aux]?

1 Ответ

0 голосов
/ 28 августа 2018

Вы неправильно понимаете, что делает 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, которые предназначены для создания подклассов сторонним кодом без слишком большого ограничения на то, какие имена может использовать подкласс.

...