There is an idiom in Python to use classmethods to provide additional ways to construct an object, where the conversion/transformation logic stays in the classmethod and the __init__() exists solely to initialize the fields. For example:
class Foo:
field1: bytes
def __init__(self, field1: bytes):
self.field1 = field1
@classmethod
def from_hex(cls, hex: str) -> Foo:
'''
construct a Foo from a hex string like "12:34:56:78"
'''
return cls(field1=bytes.fromhex(hex.replace(':', ' ')))
Now, let's say I define a class derived from Foo:
class Bar(Foo):
field2: str
def __init__(self, field1: bytes, field2: str):
Foo.__init__(self, field1)
self.field2 = field2
With this hierarchy in mind, I want to define a constructor Bar.from_hex_with_tag() that would serve as an extension of Foo.from_hex():
class Bar(Foo):
<...>
@classmethod
def from_hex_with_tag(cls, hex: str, tag: str) -> Bar:
return cls(
field1=bytes.fromhex(hex.replace(':', ' ')), # duplicated code with Foo.from_hex()
field2=tag
)
How do I reuse Foo.from_hex() in Bar.from_hex_with_tag()?
Reusing
classmethods like this relies on everything adhering to the Liskov Substitution Principle. Your subclass doesn't adhere to it (its initializer requires an extra argument), so the best you can do is factor out the type-conversion code in the parent (likely as an underscore-prefixed@staticmethodutility) so both the parent and the child can use it.For example, you might do:
so
Barcan be:If the
__init__forBarcan make thefield2argument optional (so the parentclassmethodis valid), you could make this work like so:where you just have a placeholder at construction time which you then manually replace after, but that would also allow users of your class to construct without the second argument, which may be undesirable.