object inheritance

Anand anandology at gmail.com
Fri Oct 26 12:56:11 CEST 2007


I am trying to implement some kind of object inheritance. Just like
one class can extend from another, I want to do the same on objects
dynamically.

I just thought that I can share my excitement here.

Suppose there are classes A and B and their instances a and b.

class A:
    def foo(self): self.say('foo')
    def say(self, msg):
        print 'a.say', msg

class B:
    def say(self, msg):
        print 'b.say', msg

a = A()
b = B()

I want b to inherit the behavior of a.

>>> b.extend_from(a)
>>> b.foo()
b.say foo

I looked around and found that some people talked about similar ideas,
but didn't find any concrete implementation.

I came up with the following implementation using meta-classes.

class ExtendMetaClass(type):
    def __init__(cls, *a, **kw):
        # take all attributes except special ones
        keys = [k for k in cls.__dict__.keys() if not
k.startswith('__')]
        d = [(k, getattr(cls, k)) for k in keys]

        # remove those attibutes from class
        for k in keys:
            delattr(cls, k)

        # remember then as dict _d
        cls._d = dict(d)

        def curry(f, arg1):
            def g(*a, **kw):
                return f(arg1, *a, **kw)
            g.__name__ = f.__name__
            return g

        def _getattr(self, name):
            """Get value of attribute from self or super."""
            if name in self.__dict__:
                return self.__dict__[name]
            elif name in self._d:
                value = self._d[name]
                if isinstance(value, types.MethodType):
                    return curry(value, self)
                else:
                    return value
            else:
                if self._super != None:
                    return self._super._getattr(name)
                else:
                    raise AttributeError, name

        def __getattr__(self, name):
            """Returns value of the attribute from the sub object.
            If there is no sub object, self._getattr is called.
            """
            if name.startswith('super_'):
                return self._super._getattr(name[len('super_'):])

            if self._sub is not None:
                return getattr(self._sub, name)
            else:
                return self._getattr(name)

        def extend_from(self, super):
            """Makes self extend from super.
            """
            self._super = super
            super._sub = self

        cls.__getattr__ = __getattr__
        cls._getattr = _getattr
        cls._super = None
        cls._sub = None
        cls.extend_from = extend_from

class Extend:
    __metaclass__ = ExtendMetaClass
    def __init__(self, super=None):
        if super:
            self.extend_from(super)

And the above example becomes:

class A(Extend):
    def foo(self): self.say('foo')
    def say(self, msg):
        print 'a.say', msg

class B(Extend):
    def say(self, msg):
        print 'b.say', msg
        # self.super_foo calls foo method on the super object
        self.super_say('super ' + msg)

a = A()
b = B()

>>> b.extend_from(a)
>>> b.foo()
b.say foo
a.say super foo

There are one issue with this approach. Once b extends from a,
behavior of a also changes, which probably should not. But that
doesn't hurt me much.

Any comments?




More information about the Python-list mailing list