I have some data I need to marshal, of which there are two main types with some base behavior. In order to avoid all the duplicate logic of actually marshaling the data, I decided to use inheritance. I figured objects would also be a good choice since I can add type hints, and make all the arguments required (say vs. a dict
, which is what we are currently using and is prone to typos/field omission).
I am also aware of TypedDict
but since there is a tiny bit of behavior associated with the data I didn't feel it would be a good choice. A dataclass
seems like a good middle ground except it's not so great with inheritance and exposes some fields that should remain hidden from the caller.
All I really care about is enforcing required arguments and types.
So let's say I have these classes
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)
This is fine when there are three arguments, but some have ten, and there's just a ton of boilerplate. Is there a way to remove all the arg passing to Base
, or even just collect args
into kwargs
and pass that down?
CodePudding user response:
You could use the default method of passing all arguments in python:
class A(Base):
def __init__(self, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
This is standard python to catch and then pass all arguments.
However, separate question for you: are you needing to define your own init processing steps?
...In your example above, your child class's init
isn't doing anything except calling the parent's init
.
If you don't define an __init__
method in a child class, when an instance is created python will automatically call __init__
in the parent class. So you don't need to define init
anywhere unless you need to for some class specific steps.
Eg:
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
CodePudding user response:
A class should extract the keyword arguments it expects, and pass the rest upstream, assuming that somebody needs them. It's the caller's responsibility to pass all required arguments.
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())
If you really want A.__init__
to hard-code foo_id
as the id
attribute for Base
, you can:
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())