Получить тип суперкласса в Python 3 - PullRequest
0 голосов
/ 03 января 2019

У меня есть базовый класс с двумя производными классами. Я хочу, чтобы методы базовых классов вели себя по-разному, в зависимости от того, относятся ли аргументы к тому же типу, что и производный класс, или только к экземплярам базового класса, но другого типа. Это текущая реализация:

class MyBase:
    def __init__(self, foo: int):
        self.foo = foo 

    def __eq__(self, other):
        return self.foo == other.foo 


class MyDerived_1(MyBase):
    def __init__(self, foo: int, bar: int):
        super().__init__(foo)
        self.bar = bar


class MyDerived_2(MyBase):
    def __init__(self, foo: int, bar: int):
        super().__init__(foo)
        self.bar = bar 

    def __eq__(self, other):
        if type(other) == type(self):
            return self.bar == other.bar 
        elif isinstance(other, MyBase):
            return super().__eq__(other)
        else:
            return False

В четвертой последней строке я должен явно ссылаться на MyBase. Возможно, это нормально, но я понимаю, что основной смысл ключевого слова "super" заключается в том, что оно должно позволять вам изменять базовый класс без необходимости что-либо переписывать в классе. То есть потенциальная проблема с этим решением состоит в том, что если MyBase будет изменен, то init будет в порядке, потому что он вызывает «super», но eq не будет обновлять свое поведение.

Поэтому я попытался заменить «MyBase» на «type (super)» или «type (super ())», но они не ссылаются на суперкласс, они ссылаются на класс объекта «super».

Обратите внимание, что этот вопрос отличается от:

Получить имя родительского класса? Получить определяющий класс несвязанного объекта метода в Python 3 и т.д.

Потому что они ищут родительские классы после инициализации объекта.

Полагаю, я смогу найти суперкласс, запустив MRO. Но это кажется плохим решением, учитывая, что я не ищу целое дерево наследования, я просто хочу знать тип суперкласса.

Есть ли способ извлечь эту информацию из "супер"?

Ответы [ 2 ]

0 голосов
/ 03 января 2019

Я думаю, вам может понадобиться использовать inspect модуль, и он getclasstree() функция: https://docs.python.org/3/library/inspect.html#inspect.getclasstree

class MyDerived_2(MyBase):

    def mytree(self):
        print(inspect.getclasstree([self.__class__]))


c = MyDerived_2(1, 2)
c.mytree()

это выводит:

[(<class '__main__.MyBase'>, (<class 'object'>,)), [(<class '__main__.MyDerived_2'>, (<class '__main__.MyBase'>,))]]
0 голосов
/ 03 января 2019

Прежде всего, вы хотите вернуть NotImplemented из __eq__, когда столкнетесь с типом, который вы не поддерживаете, так что Python также может дать второму операнду возможность участвовать в тесте на равенство. Из документации Python datamodel :

Числовые методы и методы расширенного сравнения должны возвращать это значение, если они не реализуют операцию для предоставленных операндов. (Затем интерпретатор попытается выполнить отраженную операцию или другой запасной вариант, в зависимости от оператора.)

Ваш код должен просто делегировать super().__eq__(), когда other не является экземпляром того же типа, здесь нет необходимости проверять базовый тип; базовый класс уже должен позаботиться о проверке правильного типа или протокола.

Затем вы можете использовать Python 3 __class__ замыкание для доступа к классу, в котором определен метод; Python добавляет это замыкание всякий раз, когда вы используете super() или __class__ в определении функции, вложенном в определение класса:

class MyBase:
    # ...

    def __eq__(self, other):
        if not isinstance(other, __class__):
            # we can't handle the other type, inform Python
            return NotImplemented
        return self.foo == other.foo 

class MyDerived_2(MyBase):
    # ...

    def __eq__(self, other):
        if isinstance(other, __class__):
            # if other is an instance of MyDerived_2, only test for 'bar'
            return self.bar == other.bar 
        # otherwise fall back to the base behaviour
        return super().__eq__(other)

Обратите внимание, что я использовал isinstance(), а не type() тесты, вы бы хотели подклассы из MyDerived_2 для наследования этого поведения.

Вместо того, чтобы проверять определенную иерархию классов, вы могли бы также полагаться на типизацию утки; если другой объект имеет правильные имена атрибутов, просто предположите, что его можно использовать для сравнения с:

class MyBase:
    # ...

    def __eq__(self, other):
        try:
            self.foo == other.foo
        except AttributeError:
            # we can't handle the other type, inform Python
            return NotImplemented

class MyDerived_2(MyBase):
    # ...

    def __eq__(self, other):
        try:
            self.bar == other.bar
        except AttributeError:
            # otherwise fall back to the base behaviour
            return super().__eq__(other)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...