Suppose I want to compose two objects and I'd like to be able to be able to define multiple constructors that respect the default arguments.
class A:
def __init__(x,y=0):
self.x = x
self.y = y
class B:
def __init__(w,objA, z=1):
self.w = w
self.objA = objA
self.z = z
I'd like to have multiple constructors for B
which allow me to either pass an object A
or pass parameters and then construct A
. Looking at What is a clean, pythonic way to have multiple constructors in Python? I can do:
class B:
def __init__(w, objA, z=1):
self.w = w
self.objA = objA
self.z=z
@classmethod
def from_values(cls,w,z,x,y):
objA = A(x,y)
return cls(w,objA,z)
The problem is that the default values are being overwritten and I'd like to keep them. The following of course does not work.
@classmethod
def from_values(cls,w,x,**kwargs):
objA = A(x,**kwargs)
return cls(w,objA,**kwargs)
So it seems I'm stuck with remembering the default values and calling them like this
@classmethod
def from_values(cls,w,x,z=1,y=0):
objA = A(x,y)
return cls(w,objA,z)
This is not what I want since I'd rather have the objects themselves handle the default values and not be forced remember the default values. I could do one better than the above and use:
@classmethod
def from_values(cls,w,x,z=1,**kwargs):
objA = A(x,**kwargs)
return cls(w,objA,z)
But in this case I still need to "remember" the default value for z
. Is there a Pythonic solution to this? Is this a design problem? Can someone point me to a good design pattern or best practices? This problem compounds when composing with several objects...
class L:
def __init__(objM,objN):
self.objM = objM
self.objN = objN
@classmethod
def from_values(cls, m1,m2,m3,n1,n2):
objM = M(m1,m2,m3)
objN = N(n1,n2)
return cls(objM, objN)
First I would argue that this isn't what you want to do. As this scales up, trying to call your constructor will be tedious and error-prone.
You can solve both of our problems with an explicit dictionary.
class A:
def __init__(self, config):
self.x = config.get('x')
assert self.x # if you need it
self.y = config.get('y', 0)
class B:
def __init__(self, b_config, objA):
self.w = b_config.get('w')
self.objA = objA
self.z = b_config.get('z', 1)
@classmethod
def from_values(cls,b_config,a_config):
return cls(b_config, A(a_config))
B.from_values({'w':1, 'z':2}, {'x': 3, 'y': 4})
It's probably not as clever or neat as what you're looking for, but it does let you construct from an A
if you already have it, or to pass in a configurable set of parameters in a more structured way.