Welcome, guest | Sign In | My Account | Store | Cart

Two implementations for extending an exsisting class using metaclasses

Python, 39 lines
 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'

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): ...
...

4 comments

Walker Hale 15 years, 3 months ago  # | flag

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__
Oren Tirosh 13 years, 7 months ago  # | flag

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.

David Rogers 12 years, 12 months ago  # | flag

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

Benjamin Friman 9 years, 11 months ago  # | flag

Thanks. The ExtendInplace class proved a very neat way to customise the base widget class in PySide so that all the derived widget types have a customised look and feel.

Created by Daniel Brodie on Fri, 29 Apr 2005 (PSF)
Python recipes (4591)
Daniel Brodie's recipes (5)

Required Modules

  • (none specified)

Other Information and Tasks