Second constructor when standard constructor has superfluous information - python

I have the following situation:
class Foo:
def __init__(self, O):
self.a = O.some_attr.calc_a()
self.b = O.some_other_attr.calc_b()
Note that O cannot be reconstructed from a and b. Now, I also want to be able to initialise Foo directly by passing a and b, but I only want to do this internally, the standard way should be by passing O.
I know I can do something like
class Foo:
def __init__(self, a, b):
self.a = a
self.b = b
#classmethod
def from_O(cls, O):
return cls(O.some_attr.cal_a(), O.some_other_attr.cal_b())
but this has the disadvantage that the standard call now becomes the more cumbersome Foo.from_O(O).
In other words, how can I achieve Foo(O) and Foo.from_a_b(a,b) when O is not reconstructable from a and b?
Can I have a classmethod that avoids calling __init__?
(Note: I am looking for a 'clean' way to do this. I know I can dissect the argument list or do something like
class _Foo:
def __init__(self, a, b):
self.a = a
self.b = b
class Foo(_Foo):
def __init__(self, O):
super().__init__(O.some_attr.cal_a(), O.some_other_attr.cal_b())
but this seems a rather awkward solution.)

You could make O, a, and b all optional arguments to the same __init__ method and make a distinction on whether O is given or not.
class Foo:
def __init__(self, O=None, a=None, b=None):
if O is not None:
self.a = O.some_attr.calc_a()
self.b = O.some_other_attr.calc_b()
# ignore a and b
else:
if a is None or b is None:
raise TypeError("If O is not given, a and b cannot be None")
self.a = a
self.b = b
Usage:
# from O
foo_from_O = Foo(O)
# from a, b
foo_from_a_b_1 = Foo(None, 'a', 'b')
foo_from_a_b_2 = Foo(a='a', b='b')

Related

Best way of solving diamond problem in Python with fields

Python solves the diamond problem well if there are no fields in the classes by linearizing the method resolution order. However, if the classes have fields then how do you call the super constructors? Consider:
class A:
def __init__(self, a):
self.a = a # Should only be initialized once.
class B(A):
def __init__(self, a, b):
super().__init__(a)
self.b = b
class C(A):
def __init__(self, a, c, b=None):
super().__init__(a)
self.c = c
class D(C, B):
def __init__(self, a, b, c):
super().??? # What do you put in here.
For my use case I do actually have a solution, because b can't be None in the application and therefore the following largely works:
class A:
def __init__(self, a):
self.a = a # Should only be initialized once.
class B(A):
def __init__(self, a, b):
assert b is not None # Special case of `b` can't be `None`.
super().__init__(a)
self.b = b
class C(A):
def __init__(self, a, c, b=None): # Special init with default sentinel `b`.
if b is None:
super().__init__(a) # Normally `C`'s super is `A`.
else:
super().__init__(a, b) # From `D` though, `C`'s super is `B`.
self.c = c
class D(C, B): # Note order, `C`'s init is super init.
def __init__(self, a, b, c):
super().__init__(a, c, b)
def main():
A('a')
B('b', 1)
C('c', 2)
D('d', 3, 4)
C('c2', 5, 6) # TypeError: __init__() takes 2 positional arguments but 3 were given
This largely works for the special case of b can't be None, however it still has a problem if C's __init__ is called directly (see last line of above). Also you have to modify C for the multiple inheritance and you have to inherit in the order C, B.
==== Edit ===
Another possibility is to manually initialize each field (this is somewhat similar to how Scala handles fields under the covers).
class A0:
def __init__(self, a): # Special separate init of `a`.
self._init_a(a)
def _init_a(self, a):
self.a = a
class B0(A0):
def __init__(self, a, b): # Special separate init of `b`.
self._init_a(a)
self._init_b(b)
def _init_b(self, b):
self.b = b
class C0(A0):
def __init__(self, a, c): # Special separate init of `c`.
self._init_a(a)
self._init_c(c)
def _init_c(self, c):
self.c = c
class D0(C0, B0):
def __init__(self, a, b, c): # Uses special separate inits of `a`, `b`, and `c`.
self._init_a(a)
self._init_b(b)
self._init_c(c)
The disadvantage of this approach is that it is very non-standard, to the extent that PyCharm gives a warning about not calling super init.
==== End edit ===
Is there a better way?
Thanks in advance for any help, Howard.

Passing Properties as Default Parameter

I'm trying to find an elegant pythonic solution for the following design issue:
Within a class I want to use properties as default parameter.
So, one could do something like:
Class A:
def __init__(self, a=0, b=1):
self.a = a
self.b = b
def func(self, a=None, b=None):
a = a or self.a
b = b or self.b
# do something with a and b
But since this seems like potential boilerplate code, I went ahead and wrote a generic function outside of my class.
def fallback_parameters(obj, **args):
return [args[arg] or getattr(obj, arg) for arg in args]
and use
def func(self, a=None, b=None):
a, b = fallback_parameters(self, a=a, b=b)
# do something with a and b
Is there a better/cleaner way to achieve this? An existing solution?
I'm tempted to create a decorator, so I don't need to type something like:
a, b, c, d, e = fallback_parameters(self, a=a, b=b, c=c, d=d, e=e)
Just for illustration if you wanted it to change the object instance attibute (you say you do not) the decorator would be like this:
def fallback_args(func):
def inner(obj, **kwargs):
if kwargs:
for key in kwargs:
setattr(obj, key, kwargs[key])
return func(obj, **kwargs)
return inner
class B:
def __init__(self, a=0, b=1):
self.a = a
self.b = b
#fallback_args
def func(self, a=None, b=None):
print("Do something with {} and {}".format(self.a, self.b))
#or
#a = self.a
#b = self.b
#print("Do something with {} and {}".format(a, b))
obj2 = B()
print(obj2.a)
print(obj2.b)
obj2.func()
print()
obj2 = B()
print(obj2.a)
print(obj2.b)
obj2.func(a=300)
print()
print(obj2.a)
print(obj2.b)
Outputs:
0
1
Do something with 0 and 1
0
1
Do something with 300 and 1
300
1
But not wanting to change the object, I don't think it makes sense just to avoid
explicit a = self.a or a like lines. Because if you don't list the parameters in the call, how would the decorator know what names do you mention in the methods' body to fallback/load them? You can say, the names are only the ones listed in the method signature. Now, that can be introspected, but I think I am seeing more convoluted work here than this deserves. a = self.a or a looks simple and explicit. So it is Pythonic too, although you probably could write the implicit assignment you want with some more work.
For an idea on how to introspect method args see a somewhat related decorator I wrote here to freeze methods args.

Python: How to share data between instances of different classes?

Class BigClassA:
def __init__(self):
self.a = 3
def foo(self):
self.b = self.foo1()
self.c = self.foo2()
self.d = self.foo3()
def foo1(self):
# do some work using other methods not listed here
def foo2(self):
# do some work using other methods not listed here
def foo3(self):
# do some work using other methods not listed here
Class BigClassB:
def __init__(self):
self.b = # need value of b from BigClassA
self.c = # need value of c from BigClassA
self.d = # need value of d from BigClassA
def foo(self):
self.f = self.bar()
def bar(self):
# do some work using other methods not listed here and the value of self.b, self.c, and self.d
Class BigClassC:
def __init__(self):
self.b = # need value of b from BigClassA
self.f = # need value of f from BigClassB
def foo(self):
self.g = self.baz()
def baz(self):
# do some work using other methods not listed here and the value of self.b and self.g
Question:
Basically I have 3 classes with lots of methods and they are somewhat dependent as you can see from the code. How do I share the value of instance variables self.b, self.c, self.d from BigClassA to BigClassB?
nb: these 3 classes can not be inherited from each other, since it does not make sense.
What I have in mind, is just to combine all methods into a super big class. But I don't feel this is a right way to do it.
You are correct, in your case inheritance does not make sense. But, how about explicitly passing the objects during the instantiation. This would make a lot of sense.
Something like:
Class BigClassA:
def __init__(self):
..
Class BigClassB:
def __init__(self, objA):
self.b = objA.b
self.c = objA.c
self.d = objA.d
Class BigClassC:
def __init__(self, objA, objB):
self.b = objA.b # need value of b from BigClassA
self.f = objB.f # need value of f from BigClassB
While instantiating, do:
objA = BigClassA()
..
objB = BigClassB(objA)
..
objC = BigClassC(objA, objB)

define class with variable number of inputs in python

How does one define a class constructor with two possible inputs:
class MSMeshFace(object):
def __init__(self, A=None, B=None, C=None)
def __init__(self, A=None, B=None, C=None, D=None)
So obviously this doesnt work but that's what i would like it to behave as. If someone inputs A,B,C then construct MSMeshFace from those three variables else if someone inputs A,B,C,D then construct it from all four. What's the proper way to do that so that when I call it it looks like this:
newFace = MSMeshFace(A, B, C)
or
newFace = MSMeshFace(A,B,C,D)
and they both work properly but first one creates a "triangular" face and second a "quad". I dont want to do something like newFace = MSMeshFace().Quad(A,B,C,D) if possible.
or should i do something like:
class MSMeshFace(object):
def __init__(self, a= None, b= None, c= None, d= None):
self.a = a
self.b = b
self.c = c
self.d = d
if self.d == None:
triangleFace = MSMeshFace(self.a, self.b, self.c)
return triangleFace
else:
quadFace = MSMeshFace(self.a, self.b, self.c, self.d)
return quadFace
def addData(self, data):
self.data = data
is this a valid way to construct that class?
class MSMeshFace(object):
def __init__(self, A, B, C, D=None):
if D is None:
# build mesh using (A,B,C)
else:
# build mesh using (A,B,C,D)
triFace = MSMeshFace(A,B,C)
quadFace = MSMeshFace(A,B,C,D)

Avoid specifying all arguments in a subclass

I have a class:
class A(object):
def __init__(self,a,b,c,d,e,f,g,...........,x,y,z)
#do some init stuff
And I have a subclass which needs one extra arg (the last W)
class B(A):
def __init__(self.a,b,c,d,e,f,g,...........,x,y,z,W)
A.__init__(self,a,b,c,d,e,f,g,...........,x,y,z)
self.__W=W
It seems dumb to write all this boiler-plate code, e.g passing all the args from B's Ctor to the inside call to A's ctor, since then every change to A's ctor must be applied to two other places in B's code.
I am guessing python has some idiom to handle such cases which I am unaware of. Can you point me in the right direction?
My best hunch, is to have a sort of Copy-Ctor for A and then change B's code into
class B(A):
def __init__(self,instanceOfA,W):
A.__copy_ctor__(self,instanceOfA)
self.__W=W
This would suit my needs since I always create the subclass when given an instance of the father class, Though I am not sure whether it's possible...
Considering that arguments could be passed either by name or by position, I'd code:
class B(A):
def __init__(self, *a, **k):
if 'W' in k:
w = k.pop('W')
else:
w = a.pop()
A.__init__(self, *a, **k)
self._W = w
Edit: based on Matt's suggestion, and to address gnibbler's concern re a positional-argument approach; you might check to make sure that the additional subclass-specific argument is being specified—similar to Alex's answer:
class B(A):
def __init__(self, *args, **kwargs):
try:
self._w = kwargs.pop('w')
except KeyError:
pass
super(B,self).__init__(*args, **kwargs)
>>> b = B(1,2,w=3)
>>> b.a
1
>>> b.b
2
>>> b._w
3
Original answer:
Same idea as Matt's answer, using super() instead.
Use super() to call superclass's __init__() method, then continue initialising the subclass:
class A(object):
def __init__(self, a, b):
self.a = a
self.b = b
class B(A):
def __init__(self, w, *args):
super(B,self).__init__(*args)
self.w = w
In situations where some or all of the arguments passed to __init__ have default values, it can be useful to avoid repeating the __init__ method signature in subclasses.
In these cases, __init__ can pass any extra arguments to another method, which subclasses can override:
class A(object):
def __init__(self, a=1, b=2, c=3, d=4, *args, **kwargs):
self.a = a
self.b = b
# …
self._init_extra(*args, **kwargs)
def _init_extra(self):
"""
Subclasses can override this method to support extra
__init__ arguments.
"""
pass
class B(A):
def _init_extra(self, w):
self.w = w
Are you wanting something like this?
class A(object):
def __init__(self, a, b, c, d, e, f, g):
# do stuff
print a, d, g
class B(A):
def __init__(self, *args):
args = list(args)
self.__W = args.pop()
A.__init__(self, *args)

Categories