Передача аргументов и кваргов базовому объекту python

#python #inheritance #keyword-argument #python-dataclasses

Вопрос:

У меня есть некоторые данные, которые мне нужно собрать, из которых есть два основных типа с некоторым базовым поведением. Чтобы избежать всей повторяющейся логики фактического маршалинга данных, я решил использовать наследование. Я решил, что объекты также будут хорошим выбором , так как я могу добавлять подсказки типа и приводить все необходимые аргументы (скажем, против a dict , который мы в настоящее время используем и склонен к опечаткам/пропуску полей). Я также знаю об TypedDict этом, но, поскольку с данными связана небольшая часть поведения, я не чувствовал, что это был бы хороший выбор. A dataclass кажется хорошей серединой, за исключением того, что он не так хорош с наследованием и раскрывает некоторые поля, которые должны оставаться скрытыми от вызывающего абонента. Все, что меня действительно волнует, — это соблюдение требуемых аргументов и типов.

Итак, допустим, у меня есть эти занятия

 class Base:  def __init__(self, id: str, **kwargs):  self.id = id # the caller doesn't need to know about this field.  # data really just needs to be collected into this dict with arg names as keys  self.properties = kwargs or {}   class A(Base):  def __init__(self, a: str, b: str, c: datetime):  super().__init__(foo_id, a=a, b=b, c=c)  

Это нормально, когда есть три аргумента, но у некоторых их десять, и есть просто тонна шаблонов. Есть ли способ удалить все аргументы , передающиеся в Base , или даже просто собрать args в kwargs и передать это вниз?

Комментарии:

1. Это кажется обратным. Обычный совет состоит в том, что каждый класс определяет конкретные аргументы , которые они ожидают, и принимает произвольные аргументы ключевых слов, которые он будет передавать super().__init__ , предполагая, что кто -то выше по течению хочет их получить.

2. Вопрос в том, кому требуется a , b , и c , A , или Base ? Если A ,он должен просто добавить их к self.properties себе. ( dict Гарантируется, что он будет существовать после super().__init__ возврата.)

Ответ №1:

Вы можете использовать метод по умолчанию для передачи всех аргументов в python:

  class A(Base):  def __init__(self, *args, **kwargs) -gt; None:  super().__init__(*args, **kwargs)  

Это стандартный python для перехвата, а затем передачи всех аргументов.

Тем не менее, отдельный вопрос для вас: вам нужно определить свои собственные этапы обработки инициализации?

…В приведенном выше примере ваш дочерний класс init ничего не делает, кроме вызова родительского init .

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

Напр.:

 class Base:   def __init__(self, arg1 = "argument one"):   self.arg1 = arg1      class Derived(Base):   pass    b = Base()  print b.arg1    d = Derived()  print d.arg1   # output: argument one  argument one   

Ответ №2:

Класс должен извлечь аргументы ключевого слова, которые он ожидает, и передать остальные вверх по течению, предполагая, что они кому-то нужны. Ответственность за передачу всех необходимых аргументов лежит на вызывающем абоненте.

 class Base:  def __init__(self, *, id: str, **kwargs):  super.__init__(**kwargs)  self.id = id  self.properties = {}  class A(Base):  def __init__(self, *, a: str, b: str, c: datetime, **kwargs):  super().__init__(**kwargs)  # self.properties is guaranteed to exist at this point  self.properties['a'] = a  self.properties['b'] = b  self.properties['c'] = c  a = A(id=foo_id, a="baz", b="bar", c=datetime.datetime.now())  

Если вы действительно хотите A.__init__ жестко закодировать foo_id в качестве id атрибута для Base , вы можете:

 class A(Base):  def __init__(self, *, a: str, b: str, c: datetime, **kwargs):  super().__init__(id=foo_id, **kwargs)  self.properties['a'] = a  self.properties['b'] = b  self.properties['c'] = c  a = A(a="baz", b="bar", c=datetime.datetime.now())