Python Programming/Metaclasses
< Python ProgrammingIn Python, classes are themselves objects. Just as other objects are instances of a particular class, classes themselves are instances of a metaclass.
Python3
The Pep 3115 defines the changes to python 3 metaclasses. In python3 you have a method __prepare__ that is called in the metaclass to create a dictionary or other class to store the class members.[1] Then there is the __new__ method that is called to create new instances of that class. [2]
Class Factories
The simplest use of Python metaclasses is a class factory. This concept makes use of the fact that class definitions in Python are first-class objects. Such a function can create or modify a class definition, using the same syntax one would normally use in declaring a class definition. Once again, it is useful to use the model of classes as dictionaries. First, let's look at a basic class factory:
>>> def StringContainer():
... # define a class
... class String:
... def __init__(self):
... self.content_string = ""
... def len(self):
... return len(self.content_string)
... # return the class definition
... return String
...
>>> # create the class definition
... container_class = StringContainer()
>>>
>>> # create an instance of the class
... wrapped_string = container_class()
>>>
>>> # take it for a test drive
... wrapped_string.content_string = 'emu emissary'
>>> wrapped_string.len()
12
Of course, just like any other data in Python, class definitions can also be modified. Any modifications to attributes in a class definition will be seen in any instances of that definition, so long as that instance hasn't overridden the attribute that you're modifying.
>>> def DeAbbreviate(sequence_container):
... sequence_container.length = sequence_container.len
... del sequence_container.len
...
>>> DeAbbreviate(container_class)
>>> wrapped_string.length()
12
>>> wrapped_string.len()
Traceback (most recent call last):
File "<stdin>", line 1, in ?
AttributeError: String instance has no attribute 'len'
You can also delete class definitions, but that will not affect instances of the class.
>>> del container_class
>>> wrapped_string2 = container_class()
Traceback (most recent call last):
File "<stdin>", line 1, in ?
NameError: name 'container_class' is not defined
>>> wrapped_string.length()
12
The type Metaclass
The metaclass for all standard Python types is the "type" object.
>>> type(object)
<type 'type'>
>>> type(int)
<type 'type'>
>>> type(list)
<type 'type'>
Just like list, int and object, "type" is itself a normal Python object, and is itself an instance of a class. In this case, it is in fact an instance of itself.
>>> type(type)
<type 'type'>
It can be instantiated to create new class objects similarly to the class factory example above by passing the name of the new class, the base classes to inherit from, and a dictionary defining the namespace to use.
For instance, the code:
>>> class MyClass(BaseClass):
... attribute = 42
Could also be written as:
>>> MyClass = type("MyClass", (BaseClass,), {'attribute' : 42})
Metaclasses
It is possible to create a class with a different metaclass than type by setting its __metaclass__ attribute when defining. When this is done, the class, and its subclass will be created using your custom metaclass. For example
class CustomMetaclass(type):
def __init__(cls, name, bases, dct):
print "Creating class %s using CustomMetaclass" % name
super(CustomMetaclass, cls).__init__(name, bases, dct)
class BaseClass(object):
__metaclass__ = CustomMetaclass
class Subclass1(BaseClass):
pass
This will print
Creating class BaseClass using CustomMetaclass Creating class Subclass1 using CustomMetaclass
By creating a custom metaclass in this way, it is possible to change how the class is constructed. This allows you to add or remove attributes and methods, register creation of classes and subclasses creation and various other manipulations when the class is created.
More resources
- Wikipedia article on Aspect Oriented Programming
- Unifying types and classes in Python 2.2
- O'Reilly Article on Python Metaclasses