ActiveState Code

Recipe 412717: Extending Classes


Two implementations for extending an exsisting class using metaclasses

Python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
def get_obj(name): return eval(name)

class ExtendReplace(type):
    def __new__(self, name, bases, dict):
        prevclass = get_obj(name)
        del dict['__module__']
        del dict['__metaclass__']
        dict.update(prevclass.__dict__)
        ret =  type.__new__(self, name, prevclass.__bases__, dict)
        return ret

class ExtendInplace(type):
    def __new__(self, name, bases, dict):
        prevclass = get_obj(name)
        del dict['__module__']
        del dict['__metaclass__']

        # We can't use prevclass.__dict__.update since __dict__
        # isn't a real dict
        for k,v in dict.iteritems():
            setattr(prevclass, k, v)
        return prevclass

class Test:
    __metaclass__=ExtendReplace
    def test_extend1(self): return 'Test Extend 1'

ext1 = Test()
assert ext1.test()=='Test'
assert ext1.test_extend1()=='Test Extend 1'

class Test:
    __metaclass__=ExtendInplace
    def test_extend2(self): return 'Test Extend 2'

ext2 = Test()
assert ext2.test()=='Test'
assert ext2.test_extend1()=='Test Extend 1'
assert ext2.test_extend2()=='Test Extend 2'

Discussion

This is a great way to split class definitions or to extend an exsisting class from a different module. I didn't approach this problem for the sake of using it, but just for the sake of seeing if I could have python do it. Well, it did, and very nicely. If you have a good use for this please say so. :)

The two different implementations approach the problem from different angles. The first creates a new class based on the previous class. I believe the previous class should be garbage collected but I am not sure. It has the benefit of not modifying the original class and therefore wont ruin already exsisting instances of the class. This behavior is more of extending a class but not for splitting a definition.

The second just adds the new definitions to the exsisting previous class. Any instance of the class before the extension will be modified to include the changes! This approach has the benefit of not creating more types, and fits much better into splitting up a class definition up.

Example of usage:

Basic definition

class 3dPerson(EnviormentObject): def __init__(self): ...

name = property(....)
....
3d Code

...

Continue definition

class 3dPerson: # Notice I do not repeat the base class __metaclass__ = ExtendInplace

def draw(surface): ....
....
Behavioral code

...

Continue definition

class 3dPerson: __metaclass__ = ExtendInplace

def find_optimal_path(destination): ...
...

Comments

  1. 1. At 12:11 p.m. on 24 aug 2006, Walker Hale said:

    Missing Demo Code. This recipe generates errors due to missing the original base class. Something like this will suffice:

    class Test( object ):
        def test(self): return self.__class__.__name__
    
  2. 2. At 5:30 a.m. on 28 apr 2008, Oren Tirosh said:

    Another possible use. Another possible use for this technique is as an alternative implementation of this recipe:

    http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/160164

    Instead of tracking all known instances of a class to fix them up on reload() the actual class could be modified in-place by injecting a custom __metaclass__ into the module being reloaded.

  3. 3. At 8:56 a.m. on 8 dec 2008, David Rogers said:

    This works great for creating a class that spans multiple source files, and is intended for advanced users to build upon.

    wantye 1. A rope or band used to fasten the pack on a pack-saddle or a load on the back of a horse. Obs. OED

Sign in to comment