При вызове super () .__ init__ как я могу определить, является ли super init объектом .__ init__ в python3? - PullRequest
0 голосов
/ 09 июля 2020

Учитывая произвольное наследование классов, как мне узнать, super().__init__ == object.__init__?

Описание + Пример

У меня есть этот код, к которому мне не разрешено касаться, который определяет классы A, B, C, CombinedCba, CombinedA c и каждый класс имеет это странное ограничение __init__, которое проверяет свойства экземпляра. Когда вызовы init выполняются во всех базовых классах, мы получаем ошибку:

TypeError: object.__init__() takes exactly one argument (the instance to initialize)

Итак, чтобы предотвратить эту ошибку, мы должны прекратить вызов super init, когда это объект init. Я могу редактировать функцию super_init. Как определить, когда super init - это init? Если я знаю, что не могу сделать следующий вызов super init и устранить ошибку.

# code that I can edit
def super_init(self, super_instance, *args, **kwargs):
  # checking super_instance.__init__ == object.__init__ OR super_instance.__init__ is object.__init__ doesn't work
  # pseudo-code
  if super_instance.__init__ is not object.__init__:
    super_instance.__init__(*args, **kwargs)

# auto generated code is from here on down
class A:
  def __init__(self, *args, **kwargs):
    self.a = kwargs['a']
    assert self.a == 'a'
    super_init(self, super(), *args, **kwargs)

class B:
  def __init__(self, *args, **kwargs):
    self.b = kwargs['b']
    self.some_num = kwargs['some_num']
    assert self.some_num <= 30
    super_init(self, super(), *args, **kwargs)

class C:
  def __init__(self, *args, **kwargs):
    self.some_num = kwargs['some_num']
    assert self.some_num >= 10
    super_init(self, super(), *args, **kwargs)

class CombinedCba(C, B, A):
  pass

combo_cba = CombinedCba(a='a', b='b', some_num=25)


class CombinedAc(A, C):
  pass

combo_ac = CombinedAc(a='a', some_num=15)

Ответы [ 2 ]

0 голосов
/ 13 июля 2020

Сначала определите A, B и C, чтобы правильно использовать super:

class A:
    def __init__(self, a, **kwargs):
        super().__init__(**kwargs)
        assert a == 'a'
        self.a = a

class B:
    def __init__(self, b, some_num, *args, **kwargs):
        super().__init__(**kwargs)
        self.b = b
        self.some_num = some_num
        assert self.some_num <= 30

class C:
    def __init__(self, some_num, **kwargs):
        super().__init__(**kwargs)
        self.some_num = some_num
        assert self.some_num >= 10

В частности, обратите внимание, что оба B и C заявляют "право собственности" "из some_num, не беспокоясь о том, что другой класс может его использовать.

Затем определите смешанный класс, который ничего не делает, но гарантирует, что some_num используется для установки атрибута some_num .

class SomeNumAdaptor:
    def __init__(self, some_num, **kwargs):
        self.some_num = some_num
        super().__init__(**kwargs)

В-третьих, определите оболочки для B и C, которые получают значение some_num из self, чтобы добавить его обратно в качестве аргумента ключевого слова (который SomeNumAdaptor лишен ):

class CWrapper(C):
    def __init__(self, **kwargs):
        super().__init__(some_num=self.some_num, **kwargs)

class BWrapper(B):
    def __init__(self, **kwargs):
        super().__init__(some_num=self.some_num, **kwargs)

Это означает, что и B, и C "сбросят" значение self.num.

(Обертка не нужна, если вы также можете измените B и C, чтобы сделать some_num необязательным, и проверьте наличие self.some_num.)

Наконец, определите свои комбинированные классы в терминах SomeNumAdaptor и классов-оболочек. Вы должны унаследовать от SomeNumAdaptor первый , чтобы BWrapper и CWrapper находили some_num как атрибут, независимо от их относительного порядка.

class CombinedAbc(SomeNumAdaptor, A, BWrapper, CWrapper):
    pass


class CombinedCba(SomeNumAdaptor, CWrapper, BWrapper, A):
    pass

combo_cba = CombinedCba(a='a', b='b', some_num=25)
combo_abc = CombinedAbc(a='a', b='b', some_num=15)

Все вышеизложенное предполагает, что ни B, ни C не хранят измененное значение своего some_num аргумента атрибута. Если это так, вам понадобятся более сложные обертки для его обработки, вероятно, они будут делать больше, чем просто передавать полученное значение в __init__. Обратите внимание, что это может указывать на более фундаментальную проблему с наследованием от B и C одновременно.

0 голосов
/ 09 июля 2020

Единственный способ, которым я смог это получить, - это создать новый временный класс, содержащий оставшиеся суперклассы, а затем проверить метод __init__ этого класса.

def super_init(self, super_instance, *args, **kwargs):
    classes_in_order = self.__class__.__mro__
    for i, cls in enumerate(classes_in_order):
        if cls == super_instance.__thisclass__:
            remainder_cls = type('_unused', classes_in_order[i+1:], {})
    super_init_is_object_init = remainder_cls.__init__ == object.__init__
    if not super_init_is_object_init:
        super_instance.__init__(*args, **kwargs)

Эти попытки не работает:

  • проверка super().__init__ == object.__init__ не работает
  • проверка super().__init__ is object.__init__ не работает
  • проверка super(super().__thisclass__, self) vs object.__init__ не работает работа
  • анализ сигнатур функций с помощью inspect.signature не сработал
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...