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())
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')
Let's say I have a class called Adder:
class adder(object):
def __init__(self, a, b):
self.a=a
self.b=b
self.result = None
def perform_addition(self):
self.result = self.a + self.b
return self.result
If I instantiate this class:
myAdder = adder(1,2)
Then the value of myAdder.result depends on calling perform_addition() first, otherwise it'll always remain None. It other words, there's a dependency on perform_addition() for the value of self.result. And if we extrapolate, a more complex class can have a chain of dependencies: ie, you have to call functions A, B, and C before D, because they in turn populate the necessary variables that the next function needs.
Is this bad class design? What is the remedy for it?
I think the above is a example of: https://en.wikipedia.org/wiki/Sequential_coupling
I think it all depends on what you want to do and how you want to go about it. the code you have is not necessarily bad, if you want a static dependency on 'perform_addition()' for the value of 'self.result' . But if you want a dynamic dependency, then the code below will be a good and simple approach. this way when an object is created by instantiating the class with values 'a' and 'b', 'self.result' will be automatically computed. you could also use more advanced tools like properties, decorators, descriptors etc. like i said, it all depends on what you want.
Class adder(object):
def __init__(self, a, b):
self.a=a
self.b=b
self.result = self.perform_addition()
def perform_addition(self):
self.result = self.a + self.b
return self.result
This would be a good case to make result a property instead, so that the addition is only performed when the result attribute is accessed:
class adder(object):
def __init__(self, a, b):
self.a = a
self.b = b
#property
def result(self):
return self.a + self.b
myAdder = adder(1,2)
print(myAdder.result)
This outputs: 3
In case the result attribute is expected to be accessed multiple times and that the calculation involved is expensive, you can save the result in an instance variable to avoid re-calculations:
class adder(object):
def __init__(self, a, b):
self.a = a
self.b = b
self._result = None
#property
def result(self):
if self._result is None:
self._result = self.a + self.b
return self._result
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.