Intercepting class method invocations using metaclass programming in Python
In Ruby, objects have a handy method called method_missing which allows one to handle method calls for methods that have not been defined. Most examples out there explain how to implement this in Python using __getattr__, however, none of them (and I mean none) explain how to intercept class method invocations using __metaclass__.
And that's why I wrote this post.
The function type is the built-in metaclass Python uses, it not only lets you know the type of an object, but also create classes on the fly. When you write, for example: class Example(object) the class object Example is not created in memory straight away. Python looks for the __metaclass__ attribute in the class definition and if it finds it, it uses it to create the object class Example. If it doesn't, it uses type to create the class. The main purpose of a metaclass is to change the class automatically, when it's created.
Here's an example of how to use metaclass programming for intercepting class method calls like the method_missing technique in Ruby:class MethodInterceptor(type): def __getattr__(cls, name): def intercept(*args, **kwargs): return cls.static_method_missing(name, *args, **kwargs) return intercept() def static_method_missing(cls, method_name, *args, **kwargs): e = "type object 'static.%s' has no attribute '%s'" \ % (cls.__name__, method_name) raise AttributeError(e)class Example(object):__metaclass__ = MethodInterceptor def __getattr__(self, name): def intercept(*args, **kwargs): return self.method_missing(name, *args, **kwargs) return intercept() def method_missing(self, method_name, *args, **kwargs): e = "type object '%s' has no attribute '%s'" \ % (self.__class__.__name__, method_name) raise AttributeError(e) @classmethod def static(cls): print 'static.%s' % cls.__name__ def instance(self): print self.__class__.__name__
Example:Example.static() static.Example Example.foo() Traceback (most recent call last): ... File "example.py", line 12, in static_method_missing raise AttributeError(e) AttributeError: type object 'static.Example' has no attribute 'foo' e = Example() e.instance() Example e.foo() Traceback (most recent call last): ... File "example.py", line 26, in method_missing raise AttributeError(e) AttributeError: type object 'Example' has no attribute 'foo'
If you ever implement something like this, remember that Python doesn't distinguish between methods and attributes the way Ruby does. There is no difference in Python between properties and methods. A method is just a property, whose type is just instancemethod.
Filed under: Programming, Python