views:

87

answers:

3

Basically, what I want is to do this:

class B:
    def fn(self):
        print 'B'

class A:
    def fn(self):
        print 'A'

@extendInherit
class C(A,B):
    pass

c=C()
c.fn()

And have the output be

A
B

How would I implement the extendInherit decorator?

+1  A: 

I personally wouldn't try doing this with a decorator since using new-style classes and super(), the following can be achieved:

>>> class A(object):
...     def __init__(self):
...         super(A, self).__init__()
...         print "A"
... 
>>> class B(object):
...     def __init__(self):
...         super(B, self).__init__()
...         print "B"
... 
>>> class C(A, B):
...     def __init__(self):
...         super(C, self).__init__()
... 
>>> foo = C()
B
A

I'd imagine method invocations would work the same way.

Neil Santos
However, here I still would have to define every function in the derived class. They would be single line functions, likedef fn(self): super(C, self).fn(), but I would still have to write it for every function. What I am looking for is a way to avoid that. Also, for this I would have to make every class a new-style class.
Nikwin
You should use new style classes anyway. Super is the way to go.
nikow
@Nikwin Constructor inheritance is left out of python by design, so if you want to use constructor logic in derived classes, yes, you have to call the constructors explicitly. Explicit is the name of the python game, and I can't say that I find that a Bad Thing :)
extraneon
@extraneon: I am not worried about the constructors, what I want is for the function of the derived class to call all the base class functions of the same name.
Nikwin
+4  A: 

This is not a job for decorators. You want to completely change the normal behaviour of a class, so this is actually a job for a metaclass.

import types

class CallAll(type):
    """ MetaClass that adds methods to call all superclass implementations """
    def __new__(meta, clsname, bases, attrs):
        ## collect a list of functions defined on superclasses
        funcs = {}
        for base in bases:
            for name, val in vars(base).iteritems():
                if type(val) is types.FunctionType:
                    if name in funcs:
                        funcs[name].append( val )
                    else:
                        funcs[name] = [val]

        ## now we have all methods, so decorate each of them
        for name in funcs:
            def caller(self, *args,**kwargs):
                """ calls all baseclass implementations """
                for func in funcs[name]:
                    func(self, *args,**kwargs)
            attrs[name] = caller

        return type.__new__(meta, clsname, bases, attrs)

class B:
    def fn(self):
        print 'B'

class A:
    def fn(self):
        print 'A'

class C(A,B, object):
    __metaclass__=CallAll

c=C()
c.fn()
THC4k
I actually rather like THC4k's solution better than mine. :D With the OP's qualification that he'd rather avoid `super()`, this appears to fit better. However, AFAIK, metaclasses only work with new-style classes. +1 to both nikow and extraneon's sentiments, though.
Neil Santos
+1  A: 

A metaclass is a possible solution, but somewhat complex. super can do it very simply (with new style classes of course: there's no reason to use legacy classes in new code!):

class B(object):
    def fn(self):
        print 'B'
        try: super(B, self).fn()
        except AttributeError: pass

class A(object):
    def fn(self):
        print 'A'
        try: super(A, self).fn()
        except AttributeError: pass

class C(A, B): pass

c = C()
c.fn()

You need the try/except to support any order of single or multiple inheritance (since at some point there will be no further base along the method-resolution-order, MRO, defining a method named fn, you need to catch and ignore the resulting AttributeError). But as you see, differently from what you appear to think based on your comment to a different answer, you don't necessarily need to override fn in your leafmost class unless you need to do something specific to that class in such an override -- super works fine on purely inherited (not overridden) methods, too!

Alex Martelli