super() and automatic method combination

Steven Bethard steven.bethard at gmail.com
Wed May 18 15:16:55 EDT 2005


Laszlo Zsolt Nagy wrote:
> I tested this and I realized that if you change the parameter list in 
> the descendants then it is not wise to use super.
> I'm going to publish the example below, I hope others can learn from it 
> too.
> 
[snip and fixed formatting]
> 
> Example (bad):
> 
> class A(object):
>    def f(self):
>        print "A.f called"
> class B(A):
>    def f(self,what):
>        super(B,self).f()
>        print "B.f called (%s)" % what
> class C(A):
>    def f(self):
>        super(C,self).f()
>        print "C.f called"
> class D(B,C):
>    def f(self):
>        super(D,self).f()
>        print "D.f called"
> 
> d = D()
> d.f()
> 
> Will result in:
> 
> C:/Python24/pythonw.exe -u  "C:/Python/Projects/Test4/test4.py"
> Traceback (most recent call last):
>  File "C:/Python/Projects/Test4/test4.py", line 22, in ?
>    d.f()
>  File "C:/Python/Projects/Test4/test4.py", line 17, in f
>    super(D,self).f()
> TypeError: f() takes exactly 2 arguments (1 given)

Yeah, this problem has been discussed before.  It's a restriction of 
super that the method signature may not change the number of parameters 
in this way in the inheritance hierarchy.

The above is clearly a toy example.  Do you really have a need for B to 
accept a parameter than none of the others accept?  Makes it sounds like 
B.f might be better off as a different method.  If the 'what' parameter 
is necessary for B.f, is it potentially applicable to the other f 
functions?  Could you make 'what' a paramter in the other functions that 
defaults to, say, None?

One other possible (but IMHO somewhat ugly) solution:

py> class A(object):
...     def f(self, *args, **kwargs):
...         print 'A.f'
...
py> class B(A):
...     def f(self, what, *args, **kwargs):
...         super(B, self).f(what, *args, **kwargs)
...         print 'B.f', what
...
py> class C(A):
...     def f(self, *args, **kwargs):
...         super(C, self).f(*args, **kwargs)
...         print 'C.f'
...
py> class D(B, C):
...     def f(self, what, *args, **kwargs):
...         super(D, self).f(what, *args, **kwargs)
...         print 'D.f', what
...
py> d = D()
py> d.f(42)
A.f
C.f
B.f 42
D.f 42
py> d.f(what=13)
A.f
C.f
B.f 13
D.f 13

The problem is that you need to know when you create A that some of the 
methods in the subclasses might change the signature.  Or you need to do 
this to every method, which is kinda nasty.

Definitely take a moment to read Guido's comments on this issue:

http://mail.python.org/pipermail/python-dev/2005-January/050656.html

The main point is that by adding parameters to functions in a subclass, 
you violate the Liskov Substitutability Principle.

STeVe



More information about the Python-list mailing list