class A(object):
def f(self):
print 'A'
class B(object):
def f(self):
print 'b'
class C(A,B):
def f(self):
super(c,self).f()
print 'C'
def test(cls):
x = cls()
x.f()
test(C)
I have the impression that this is supposed to call the f method
in both A and B, so it should print
A
B
C
or maybe
B
A
C
depending on the resolution order. However, it only calls A.f and not B.f.
I also notice that if I say
class B(object):
def f(self):
super(B,self).f()
print 'b'
then
test(B)
raises an exception since B has no superclass with an f method. That
doesn't seem like such a good thing necessarily.
Anyway, is there a preferred way of writing this example so that C.f
automatically calls both A.f and B.f?
It would be nice to make some decorators to do CLOS-like automatic
method combination, something like:
class C(A,B):
@aftermethod
def f(self):
print 'C'
test(C)
would call A.f and B.f and then call C.f.
class C(A,B):
@beforemethod
def f(self):
print 'C'
test(C)
would call C.f and then call A.f and B.f. This would not be exactly
the same as calling super, since it should not be an error to call a
beforemethod or aftermethod when the superclass doesn't have its own
method for that operation. (I'm not sure how CLOS does this. I've
played with Flavors (a forerunner of CLOS) but have never actually
used CLOS).
> A
> B
> C
>or maybe
> B
> A
> C
>depending on the resolution order. However, it only calls A.f and not B.f.
>
>I also notice that if I say
>
>
> class B(object):
> def f(self):
> super(B,self).f()
> print 'b'
>
>then
> test(B)
>raises an exception since B has no superclass with an f method.
>
Correct. When you use super(B,self) it accesses the current instance as
the class B. If it has no method named 'f' then this will end up in an
exception.
>That doesn't seem like such a good thing necessarily.
>
>
But yes, it is. When you try to call a nonexistent method, it should
raise an exception.
>Anyway, is there a preferred way of writing this example so that C.f
>automatically calls both A.f and B.f?
>
>
I do not know a preferred way. However, I made this example for you, I
hope it helps.
class CallSupersMixin(object):
def callsupers(self,fname,*args,**kwargs):
l = self.__class__.__bases__
for cls in l:
if hasattr(cls,fname):
getattr(cls,fname)(self,*args,**kwargs)
elif cls == CallSupersMixin:
pass
else:
raise AttributeError("Base class %s does not have a
method named %s " % ( str(cls),fname ) )
class A(object):
def f(self):
print 'A.f called'
class B(object):
def f(self):
print 'B.f called'
class AB(A,B,CallSupersMixin):
def f(self,*args,**kwargs):
self.callsupers('f',*args,**kwargs)
ab = AB()
ab.f()
Of course you can remove the "raise AttributeError" part. Then it will
call only the classes that have the given method.
I know it is not a very good example but you can go from here.
Best,
Laci 2.0
--
_________________________________________________________________
Laszlo Nagy web: http://designasign.biz
IT Consultant mail: gan...@geochemsource.com
Python forever!
> I'm trying the super() function as described in Python Cookbook, 1st
> ed, p. 172 (Recipe 5.4).
>
> class A(object):
> def f(self):
> print 'A'
>
>
> class B(object):
> def f(self):
> print 'b'
>
>
> class C(A,B):
> def f(self):
Typo? 'c' should be 'C' in:
> super(c,self).f()
> print 'C'
>
> def test(cls):
> x = cls()
> x.f()
>
> test(C)
>
> I have the impression that this is supposed to call the f method
> in both A and B, so it should print
> A
> B
> C
> or maybe
> B
> A
> C
> depending on the resolution order. However, it only calls A.f and not
> B.f.
You misunderstand. A single call to super only calls the next method in the
chain. You have to include calls to super at all levels except for the
ultimate base class.
>
> I also notice that if I say
>
>
> class B(object):
> def f(self):
> super(B,self).f()
> print 'b'
>
> then
> test(B)
> raises an exception since B has no superclass with an f method. That
> doesn't seem like such a good thing necessarily.
You have to terminate the chain somehow. e.g.
class MyBase(object):
def f(self):
print "MyBase"
class A(MyBase):
def f(self):
super(A, self).f()
print "A"
...
class B(MyBase):
def f(self):
super(B, self).f()
print "B"
...
>
> Anyway, is there a preferred way of writing this example so that C.f
> automatically calls both A.f and B.f?
The trick is that C.f only calls A.f, but A.f needs to end up calling B.f
when it is used in a C.
BTW I prefer to call the base methods in this form:
class AB(A,B):
def f(self):
A.f(self)
B.f(self)
This arises the question: is there a difference between these:
super(A,self).f() # I do not use to use this....
A.f(self)
You can't do that with decorators (I mean the automatic
call of the supermethod) but you can with a metaclass.
There is an example in my ACCU lectures:
http://www.reportlab.org/~andy/accu2005/pyuk2005_simionato_wondersofpython.zip
You can also define a custom super that
does not give an error when the superclass doesn't have the
corresponding method (I posted an example
some time ago to somebody complaining for the same reason).
Michele Simionato
>
>>The trick is that C.f only calls A.f, but A.f needs to end up calling
>>B.f when it is used in a C.
>>
>>
> I believe your response only applies to single inheritance. For
> classes with muliple bases classes, you need to call the base methods
> one by one.
super wouldn't be much use if it only applied to single inheritance.
If you have a class hierarchy:
class Base(object):
class A(Base):
class B(Base):
class AB(A, B):
and each class has a method 'f' then in an instance of AB:
super(AB, self).f() --> calls A.f()
super(A, self).f() --> calls B.f()
super(B, self).f() --> calls Base.f()
but in an instance of A:
super(A, self).f() --> calls Base.f()
>
> BTW I prefer to call the base methods in this form:
>
> class AB(A,B):
> def f(self):
> A.f(self)
> B.f(self)
Which is fine so long as nobody else tries to add further subclasses later:
class C(B): ...
class Mine(AB,C): ...
Mine().f()
Using super throughout this works (it calls f in Mine, AB, A, C, B, and
then Base), but your explicit call to the base classes means that if you
don't call C.f() explicitly from Mine it never gets called, and if you do
call it explicitly from Mine it gets called *after* B.f() has been called
(and B.f() probably ends up being called twice).
>
> This arises the question: is there a difference between these:
>
> super(A,self).f() # I do not use to use this....
> A.f(self)
>
They are totally different. Super passes the call along to the next method
in the defined method resolution order (MRO): the only thing you can be
sure of here is that super(A,self).f() will never call the method f defined
in class A (whereas A.f() calls the method f defined in class A or one of
its base classes).
class Bottom(object):
def f(self):
print 'Bottom'
class A(Bottom):
def f(self):
print 'A',
super(A, self).f()
class B(Bottom):
def f(self):
print 'B',
super(B, self).f()
class C(A, B):
def f(self):
print 'C',
super(C, self).f()
C().f()
C A B Bottom
-------
Versus:
-------
class Bottom(object):
def f(self):
print 'Bottom'
class A(Bottom):
def f(self):
print 'A',
Bottom.f(self)
class B(Bottom):
def f(self):
print 'B',
Bottom.f(self)
class C(A, B):
def f(self):
print 'C',
A.f(self)
B.f(self)
C().f()
C A Bottom
B Bottom
--Scott David Daniels
Scott....@Acm.Org
You want a super object that doesn't raise an exception if the
superclass doesn't have a particular function. Try sopmething like:
py> class mysuper(super):
... def __getattribute__(self, name):
... try:
... return super(mysuper, self).__getattribute__(name)
... except AttributeError:
... def null(*args, **kwargs):
... pass
... return null
...
py> class A(object):
... def f(self):
... mysuper(A, self).f()
... print 'A'
...
py> class B(object):
... def f(self):
... mysuper(B, self).f()
... print 'B'
...
py> class C(A, B):
... def f(self):
... mysuper(C, self).f()
... print 'C'
...
py> C().f()
B
A
C
I haven't been careful here to only replace functions with functions.
That is, I probably should make a real Null object that acts both like
the null function above and like the None object otherwise. But as long
as you're only using mysuper to get functions, this should work ok.
Personally, I would probably do what others have suggested and add a
base class with an f method from which A and B derive, but if that's not
an option, you can play around with subclassing super and probably get
something like what you want.
STeVe
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.
Example (good):
class A(object):
def f(self):
print "A.f called"
class B(A):
def f(self):
super(B,self).f()
print "B.f called"
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()
Results in:
A.f called
C.f called
B.f called
D.f called
Example (bad):
class B(A):
def f(self,what):
super(B,self).f()
print "B.f called (%s)" % what
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)
Of course you cannot tell if super(C,self).f() will call A.f or not
(when add another
subclass under class A, it will change the MRO...)
If you do not want to add any other subclasses then probably you can use
super(C,self).f('foo')
but in that case it is equivalent to
A.f(self,'foo')
Best,
Laci 2.0
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