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)
Related
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.
So let's say I have this code structure:
class Parent:
def __init__(self, a, b, c):
self.a = a,
self.b = b
self.c = c
#classmethod
def from_string(cls, some_string):
if some_string == 'duh':
return cls(a=5, b=6, c='banana')
else:
return cls(a=0, b=0, c='chicken')
def method_1(self):
#do something
def method_2(self):
#do something else
class FirstChild(Parent):
def __init__(self, a, b, c):
super().__init__(a, b, c)
def child_specific_method(self):
#do something
class SecondChild(Parent):
def __init__(self, a, b):
super().__init__(a, b)
def some_other_method(self):
#do stuff
My thinking was that I want both subclasses to have access to methods of the Parent class, but also extend its functionality. At the same time I want the Parent class to instantiate with different parameters based on the class method.
Now I'm confused as to how I would create instances of child classes? Or, more precisely, how would I create child instances when there can be different versions of the parent class?
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')
I'm trying to use the cooperative multiple inheritance pattern to resolve a problem. A very simplified version of my python 2.7 code looks like this:
class Base1(object):
def __init__(self, a, b):
self.a = a
self.b = b
def to_tuple(self):
return self.a, self.b
def to_string(self):
return '%s.%s' % self.to_tuple() # (1)
class Base2(object):
def __init__(self, c, d):
self.c = c
self.d = d
def to_tuple(self):
return self.c, self.d
def to_string(self):
return '%s-%s' % self.to_tuple() #(2)
class MyMixin(Base1, Base2):
def __init__(self, a, b, c, d):
Base1.__init__(self, a, b)
Base2.__init__(self, c, d)
def to_tuple(self):
return Base1.to_tuple(self) + Base2.to_tuple(self)
def to_string(self):
return '{}: {} '.format(Base1.to_string(self), Base2.to_string(self))
mix = MyMixin('a', 'b', 'c', 'd')
print(mix.to_string())
After writing this code, I was expecting the result:
a.b: c-d
but the code fails. When the line #(1) is run, self is a MyMixin class, not a Base1 class, so to_tuple returns 4 items.
The only way I've found to fix this is to replace the lines #(1) and #(2) above with:
return '%s.%s' % Base1.to_tuple() # (1)
return '%s.%s' % Base2.to_tuple() # (2)
and this feels terribly wrong for a number of reasons.
What am I doing wrong?
Here is what happens. When mix.to_string() is called, first, it calls Base1.to_string(self) passing a mix instance as a self, which means when to_string is called on Base1 it has an instance of MyMixin which returns ('a','b','c','d') on to_tuple call. That's why it fails, cuz tuple contains 4 items and only 2 are required by line #1.
To solve this issue try to avoid inheritance from multiple classes with the same method signatures. Use composition instead.
class MyMixin(object):
def __init__(self, a, b, c, d):
self.base1 = Base1(a, b)
self.base2 = Base2(c, d)
def to_tuple(self):
return self.base1.to_tuple(self) + self.base2.to_tuple(self)
def to_string(self):
return '{}: {} '.format(self.base1.to_string(), self.base2.to_string())
I have two classes that I would like to merge into a composite. These two classes will continue to be used standalone and I don't want to modify them.
For some reasons, I want to let my composite class creating the objects. I am thinking about something like the code below (it is just an example) but I think it is complex and I don't like it very much. I guess that it could be improved by some techniques and tricks that I ignore.
Please note that the composite is designed to manage a lot of different classes with different constructor signatures.
What would recommend in order to improve this code?
class Parent:
def __init__(self, x):
self.x = x
class A(Parent):
def __init__(self, x, a="a", b="b", c="c"):
Parent.__init__(self, x)
self.a, self.b, self.c = a, b, c
def do(self):
print self.x, self.a, self.b, self.c
class D(Parent):
def __init__(self, x, d):
Parent.__init__(self, x)
self.d = d
def do(self):
print self.x, self.d
class Composite(Parent):
def __init__(self, x, list_of_classes, list_of_args):
Parent.__init__(self, x)
self._objs = []
for i in xrange(len(list_of_classes)):
self._objs.append(self._make_object(list_of_classes[i], list_of_args[i]))
def _make_object(self, the_class, the_args):
if the_class is A:
a = the_args[0] if len(the_args)>0 else "a"
b = the_args[1] if len(the_args)>1 else "b"
c = the_args[2] if len(the_args)>2 else "c"
return the_class(self.x, a, b, c)
if the_class is D:
return the_class(self.x, the_args[0])
def do(self):
for o in self._objs: o.do()
compo = Composite("x", [A, D, A], [(), ("hello",), ("A", "B", "C")])
compo.do()
You could shorten it by removing type-checking _make_object, and letting class constructors take care of the default arguments, e.g.
class Composite(Parent):
def __init__(self, x, list_of_classes, list_of_args):
Parent.__init__(self, x)
self._objs = [
the_class(self.x, *the_args)
for the_class, the_args
in zip(list_of_classes, list_of_args)
if isinstance(the_class, Parent.__class__)
]
def do(self):
for o in self._objs: o.do()
This would also allow you to use it with new classes without modifying its code.