#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())