Inheritance vs. Composition in Python for this specific case?
Question:
Assume I have a series of classes as follows:
class A(object):
__init__(self,a):
self.a=a
class B(A):
__init__(self,a,b):
super(B,self).__init__(a)
self.b=b
class C(A):
__init__(self,a,c1,c2):
super(C,self).__init__(a)
self.c1=c1
self.c2=c2
If now D is a both B and C, it can be implemented as
class D(B,C)
__init__(self,a,b,c1,c2,d):
pass
Or D
can be understood as a combination of B
and C
class D(B,C)
__init__(self,a,b,c1,c2,d):
self.b=B(a,b)
self.c=C(a,c)
which seems a bit more complicated than the multi-inheritance case.
Well, Now let’s imaging class D2
is a combination of 5 pieces of B
and 3 pieces of C
, and class D3
is made of D2
plus an additional C
(with different parameter than the ones in D
), and D4
is made of D2
and 2 additional pieces of C
(these 2 additional C
has same parameter but different from the ones in D2
). It seems D2
is good to use composition, but D3
is good to use inheritance of D2
and C
, and D4
is good to use inheritance of D2
and C
.
Use combinations, to me, has the disadvantage that I have to write d.c.c1
rather than d.c1
(as in inheritance case)to get the parameter c
or I need to store the parameter c
in D directly(besides the one as d.c.c1
). Is there any philosophical consistent way to deal with these classes? Thanks!
Answers:
You’re bound to get some level of opinion on a question like this — My answer is to use inheritance iff your classes are designed to be inherited. Use composition in all the other cases. In practice, this usually means that if the code that you are extending exists in a realm out of your control, don’t subclass it unless their documentation talks about how you can subclass it effectively.
In this case, since it seems like you are the author of the class hierarchy, inheritance seems to make sense (though some of your assertions are incorrect — D
is not so easy to implement as you have supposed since none of the superclass __init__
will get called). You probably should familiarize yourself with the design pattern in Raymond Hettinger’s "super considered super" article
class A(object):
def __init__(self, a, **kwargs):
super(A, self).__init__(**kwargs)
self.a = a
class B(A):
def __init__(self, b, **kwargs):
super(B, self).__init__(**kwargs)
self.b = b
class C(A):
def __init__(self, c1, c2, **kwargs):
super(C, self).__init__(**kwargs)
self.c1 = c1
self.c2 = c2
now the implementation of D
is trivially simple:
class D(B, C):
pass
Note that we don’t even need to define __init__
because all of the constructors of the super classes can accept arbitrary keyword arguments. Instantiating a D
looks like:
d = D(a='a', b='b', c1='c1', c2='c2')
Assume I have a series of classes as follows:
class A(object):
__init__(self,a):
self.a=a
class B(A):
__init__(self,a,b):
super(B,self).__init__(a)
self.b=b
class C(A):
__init__(self,a,c1,c2):
super(C,self).__init__(a)
self.c1=c1
self.c2=c2
If now D is a both B and C, it can be implemented as
class D(B,C)
__init__(self,a,b,c1,c2,d):
pass
Or D
can be understood as a combination of B
and C
class D(B,C)
__init__(self,a,b,c1,c2,d):
self.b=B(a,b)
self.c=C(a,c)
which seems a bit more complicated than the multi-inheritance case.
Well, Now let’s imaging class D2
is a combination of 5 pieces of B
and 3 pieces of C
, and class D3
is made of D2
plus an additional C
(with different parameter than the ones in D
), and D4
is made of D2
and 2 additional pieces of C
(these 2 additional C
has same parameter but different from the ones in D2
). It seems D2
is good to use composition, but D3
is good to use inheritance of D2
and C
, and D4
is good to use inheritance of D2
and C
.
Use combinations, to me, has the disadvantage that I have to write d.c.c1
rather than d.c1
(as in inheritance case)to get the parameter c
or I need to store the parameter c
in D directly(besides the one as d.c.c1
). Is there any philosophical consistent way to deal with these classes? Thanks!
You’re bound to get some level of opinion on a question like this — My answer is to use inheritance iff your classes are designed to be inherited. Use composition in all the other cases. In practice, this usually means that if the code that you are extending exists in a realm out of your control, don’t subclass it unless their documentation talks about how you can subclass it effectively.
In this case, since it seems like you are the author of the class hierarchy, inheritance seems to make sense (though some of your assertions are incorrect — D
is not so easy to implement as you have supposed since none of the superclass __init__
will get called). You probably should familiarize yourself with the design pattern in Raymond Hettinger’s "super considered super" article
class A(object):
def __init__(self, a, **kwargs):
super(A, self).__init__(**kwargs)
self.a = a
class B(A):
def __init__(self, b, **kwargs):
super(B, self).__init__(**kwargs)
self.b = b
class C(A):
def __init__(self, c1, c2, **kwargs):
super(C, self).__init__(**kwargs)
self.c1 = c1
self.c2 = c2
now the implementation of D
is trivially simple:
class D(B, C):
pass
Note that we don’t even need to define __init__
because all of the constructors of the super classes can accept arbitrary keyword arguments. Instantiating a D
looks like:
d = D(a='a', b='b', c1='c1', c2='c2')