Как получить следующий родительский класс метода экземпляра из `super()` в Python

Я хотел бы знать тип экземпляра, полученного из функции super(). Я старался print(super()) а также __print(type(super()))__

class Base:
    def __init__(self):
        pass

class Derive(Base):
    def __init__(self):
        print(super())        
        print(type(super()))        
        super().__init__()

d = Derive()

Результат

<super: <class 'Derive'>, <Derive object>>
<class 'super'>

С этим результатом мне было интересно, как super().__init__() вызывает правильный конструктор.

2 ответа

Решение

Из ваших комментариев, вы хотите знать, как super знает, какой метод вызывать следующим. Super проверяет mro экземпляра, знает текущий метод класса, в котором он находится, и вызывает следующий в строке. Следующая демонстрация будет работать в Python 2 и 3, а в Python 2 она печатает имя каждого класса благодаря метаклассу, поэтому я буду использовать этот вывод:

Сначала выполните импорт и настройку, чтобы сделать печать более привлекательной:

import inspect

class Meta(type):
    def __repr__(cls):
        return cls.__name__

Затем мы определяем функцию, которая сообщает нам, что происходит на основе самого суперобъекта.

def next_in_line(supobj):
    print('The instance class: {}'.format(supobj.__self_class__))
    print('in this class\'s method: {}'.format(supobj.__thisclass__))
    mro = inspect.getmro(supobj.__self_class__)
    nextindex = mro.index(supobj.__thisclass__) + 1
    print('super will go to {} next'.format(mro[nextindex]))

Наконец, мы объявляем иерархию классов на основе примера из записи википедии о линеаризации C3, для достаточно сложного примера обратите внимание на метакласс repr не работает в Python3, но назначение атрибута не сломает его. Также обратите внимание, что мы используем полный супер вызов super(Name, self) что эквивалентно super() в Python 3, и все равно будет работать:

class O(object):
    __metaclass__ = Meta

    def __init__(self):
        next_in_line(super(O, self))
        super(O, self).__init__()

class A(O):
    def __init__(self):
        next_in_line(super(A, self))
        super(A, self).__init__()


class B(O):
    def __init__(self):
        next_in_line(super(B, self))
        super(B, self).__init__()


class C(O):
    def __init__(self):
        next_in_line(super(C, self))
        super(C, self).__init__()


class D(O):
    def __init__(self):
        next_in_line(super(D, self))
        super(D, self).__init__()


class E(O):
    def __init__(self):
        next_in_line(super(E, self))
        super(E, self).__init__()


class K1(A, B, C):
    def __init__(self):
        next_in_line(super(K1, self))
        super(K1, self).__init__()


class K2(D, B, E):
    def __init__(self):
        next_in_line(super(K2, self))
        super(K2, self).__init__()


class K3(D, A):
    def __init__(self):
        next_in_line(super(K3, self))
        super(K3, self).__init__()


class Z(K1, K2, K3):
    def __init__(self):
        next_in_line(super(Z, self))
        super(Z, self).__init__()

Теперь, когда мы печатаем mro of Z, мы получаем порядок разрешения метода, определенный этим алгоритмом, применяемый к дереву наследования:

>>> print(inspect.getmro(Z))
(Z, K1, K2, K3, D, A, B, C, E, O, <type 'object'>)

И когда мы вызываем Z(), потому что наша функция использует mro, мы будем посещать каждый метод по порядку:

>>> Z()
The instance class: Z
in this class's method: Z
super will go to K1 next
The instance class: Z
in this class's method: K1
super will go to K2 next
The instance class: Z
in this class's method: K2
super will go to K3 next
The instance class: Z
in this class's method: K3
super will go to D next
The instance class: Z
in this class's method: D
super will go to A next
The instance class: Z
in this class's method: A
super will go to B next
The instance class: Z
in this class's method: B
super will go to C next
The instance class: Z
in this class's method: C
super will go to E next
The instance class: Z
in this class's method: E
super will go to O next
The instance class: Z
in this class's method: O
super will go to <type 'object'> next

И мы останавливаемся на object.__init__, Из вышесказанного видно, что super всегда знает, в каком классе находится экземпляр, метод класса, в котором он находится в данный момент, и может определить из MRO класса экземпляра, куда идти дальше.


Я хотел бы знать название базового класса?

Если вам нужна только прямая база (или несколько, в случае множественного наследования), вы можете использовать __bases__ атрибут, который возвращает кортеж

>>> Derive.__bases__
(<class __main__.Base at 0xffeb517c>,)

>>> Derive.__bases__[0].__name__
'Base'

Я рекомендую модуль проверки для получения Порядка разрешения метода (который super следует на основе исходного класса вызывающего абонента):

>>> import inspect
>>> inspect.getmro(Derive)
(<class __main__.Derive at 0xffeb51dc>, <class __main__.Base at 0xffeb517c>)

Получая это от супер

super().__self_class__ дает экземпляр класса, и super().__thisclass__ дает нам текущий класс. Мы можем использовать MRO экземпляра и найти следующий класс. Я предполагаю, что вы не будете делать это в последнем родителе, поэтому я не улавливаю ошибку индекса:

class Base:
    def __init__(self):
        print(super().__self_class__)
        print(super().__thisclass__)


class Derive(Base):
    def __init__(self):
        print(super().__self_class__)
        print(super().__thisclass__)
        mro = inspect.getmro(super().__self_class__)
        nextindex = mro.index(super().__thisclass__) + 1
        print('super will go to {} next'.format(mro[nextindex]))
        super().__init__()


>>> d = Derive()
<class '__main__.Derive'>
<class '__main__.Derive'>
super will go to <class '__main__.Base'> next
<class '__main__.Derive'>
<class '__main__.Base'>

Вы не можете делать то, что вы хотите с super() непосредственно. Перейти в класс MRO (см. class.__mro__) вместо:

class Derive(Base):
    def __init__(self):
        mro = type(self).__mro__
        parent = mro[mro.index(__class__) + 1]
        print(parent)

Вот __class__ переменная магического замыкания *, которая ссылается на класс, в котором определена текущая функция; вышеописанное продолжает работать, даже если вы создаете подкласс или смешиваете дополнительные классы с Derive, даже когда вы производите шаблон наследования алмазов.

Демо-версия:

>>> class Base: pass
... 
>>> class Derive(Base):
...     def __init__(self):
...         mro = type(self).__mro__
...         parent = mro[mro.index(__class__) + 1]
...         print(parent)
... 
>>> Derive()
<class '__main__.Base'>
<__main__.Derive object at 0x10f7476a0>
>>> class Mixin(Base): pass
... 
>>> class Multiple(Derive, Mixin): pass
... 
>>> Multiple()
<class '__main__.Mixin'>
<__main__.Multiple object at 0x10f747ba8>

Обратите внимание, как Multiple класс наследует от обоих Derive а также Mixin и, следовательно, следующий класс в MRO Mixin не Base, так как Mixin также происходит от Base,

Это копирует то, что super() делает; найти следующий класс в MRO для экземпляра относительно текущего класса.


* Для справки, см. Почему в Python 3.x супер () магия?

Другие вопросы по тегам