Передача kwargs в цепочке наследования классов - PullRequest
0 голосов
/ 12 марта 2019

У меня есть следующие настройки:

class A:
  def __init__(self, **kwargs):
    # Some variables initialized
    for k, v in kwargs.items():
        setattr(self, k, v)

class B(A):
  def __init__(self, **kwargs):
    A.__init__(self, **kwargs)
    self._b = {}
    for k, v in kwargs.items():
        setattr(self, k, v)

    @property
    def b(self):
        return self._b

    @b.setter
    def b(self, value):
        self._b.update(value)

class C(B):
    def __init__(self, **kwargs):
    B.__init__(self, **kwargs)
    # Some variables initialized
    for k, v in kwargs.items():
        setattr(self, k, v)

Когда я сейчас создаю новый экземпляр C, я получаю следующую ошибку:

AttributeError: 'C' object has no attribute '_b'

Теперь это имеет смысл, поскольку B._b не было инициализировано при вызове A.__init__(self, **kwargs).Я могу решить эту проблему, просто переставив инициализацию B следующим образом:

class B(A):
  def __init__(self, **kwargs):
    self._b = {}
    A.__init__(self, **kwargs)
    for k, v in kwargs.items():
        setattr(self, k, v)

Я бы хотел понять, есть ли рекомендуемый / лучший метод, когда мне нужно передать kwargs от ребенкародительским классам во время инициализации?Мне кажется, что следующие вещи будут работать,

  1. Переупорядочить инициализацию, как у меня выше
  2. Назначить kwargs в каждом дочернем классе, затем вытолкнуть их и передать оставшиесяkwargs вместе с родительской инициализацией
  3. Что-то лучше

Надеемся получить несколько подходов для 3.

1 Ответ

0 голосов
/ 12 марта 2019

У вас есть проблема с этими циклами:

for k, v in kwargs.items():
    setattr(self, k, v)

У вас есть по одному в каждом классе, и это означает, что каждый один из классов устанавливает все ключевое слово аргументы в качестве атрибутов self.

Когда этот цикл выполняется в A, он завершается ошибкой, поскольку B имеет свойство, которое необходимо инициализировать, прежде чем он сможет работать.

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

class B(A):
    def __init__(self, **kwargs):
        _b = {}                        # set this up first
        A.__init__(self, **kwargs)     # before calling the superclass
        for k, v in kwargs.items():
            setattr(self, k, v)

Но, вероятно, есть лучший подход, который позволит вам избежатьизбыточные петли.Я бы предложил явно назвать ключевые аргументы, которые вы ожидаете в каждом классе.Таким образом, b будет виден только классу B, а не A и C (кроме как часть kwargs).

class A:
    def __init__(self, *, a): # a is keyword-only arg, no kwargs accepted here
        self.a = a

class B:
    def __init__(self, *, b, **kwargs):
        super().__init__(**kwargs) # doesn't mess with b!
        self._b = {}
        self.b = b

    @property
    def b(self):
       ...

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

Теперь вы можете позвонить C(a="foo", b={1: 2}, c="bar") и каждый класс будет обращать внимание только на атрибут, который ему нужен.

...