Two implementations for extending an exsisting class using metaclasses
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:
class 3dPerson(EnviormentObject): def __init__(self): ...
name = property(....) ....
class 3dPerson: # Notice I do not repeat the base class __metaclass__ = ExtendInplace
def draw(surface): .... ....
class 3dPerson: __metaclass__ = ExtendInplace
def find_optimal_path(destination): ... ...
Missing Demo Code. This recipe generates errors due to missing the original base class. Something like this will suffice:
Another possible use. Another possible use for this technique is as an alternative implementation of this recipe:
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.
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
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.