What are metaclasses and what do we use them for?
相关问题
- how to define constructor for Python's new Nam
- streaming md5sum of contents of a large remote tar
- How to get the background from multiple images by
- Evil ctypes hack in python
- Correctly parse PDF paragraphs with Python
I think the ONLamp introduction to metaclass programming is well written and gives a really good introduction to the topic despite being several years old already.
http://www.onlamp.com/pub/a/python/2003/04/17/metaclasses.html (archived at https://web.archive.org/web/20080206005253/http://www.onlamp.com/pub/a/python/2003/04/17/metaclasses.html)
In short: A class is a blueprint for the creation of an instance, a metaclass is a blueprint for the creation of a class. It can be easily seen that in Python classes need to be first-class objects too to enable this behavior.
I've never written one myself, but I think one of the nicest uses of metaclasses can be seen in the Django framework. The model classes use a metaclass approach to enable a declarative style of writing new models or form classes. While the metaclass is creating the class, all members get the possibility to customize the class itself.
The thing that's left to say is: If you don't know what metaclasses are, the probability that you will not need them is 99%.
A metaclass is a class that tells how (some) other class should be created.
This is a case where I saw metaclass as a solution to my problem: I had a really complicated problem, that probably could have been solved differently, but I chose to solve it using a metaclass. Because of the complexity, it is one of the few modules I have written where the comments in the module surpass the amount of code that has been written. Here it is...
In addition to the published answers I can say that a
metaclass
defines the behaviour for a class. So, you can explicitly set your metaclass. Whenever Python gets a keywordclass
then it starts searching for themetaclass
. If it's not found – the default metaclass type is used to create the class's object. Using the__metaclass__
attribute, you can setmetaclass
of your class:It'll produce the output like this:
And, of course, you can create your own
metaclass
to define the behaviour of any class that are created using your class.For doing that, your default
metaclass
type class must be inherited as this is the mainmetaclass
:The output will be:
Classes as objects
Before understanding metaclasses, you need to master classes in Python. And Python has a very peculiar idea of what classes are, borrowed from the Smalltalk language.
In most languages, classes are just pieces of code that describe how to produce an object. That's kinda true in Python too:
But classes are more than that in Python. Classes are objects too.
Yes, objects.
As soon as you use the keyword
class
, Python executes it and creates an OBJECT. The instructioncreates in memory an object with the name "ObjectCreator".
This object (the class) is itself capable of creating objects (the instances), and this is why it's a class.
But still, it's an object, and therefore:
e.g.:
Creating classes dynamically
Since classes are objects, you can create them on the fly, like any object.
First, you can create a class in a function using
class
:But it's not so dynamic, since you still have to write the whole class yourself.
Since classes are objects, they must be generated by something.
When you use the
class
keyword, Python creates this object automatically. But as with most things in Python, it gives you a way to do it manually.Remember the function
type
? The good old function that lets you know what type an object is:Well,
type
has a completely different ability, it can also create classes on the fly.type
can take the description of a class as parameters, and return a class.(I know, it's silly that the same function can have two completely different uses according to the parameters you pass to it. It's an issue due to backwards compatibility in Python)
type
works this way:e.g.:
can be created manually this way:
You'll notice that we use "MyShinyClass" as the name of the class and as the variable to hold the class reference. They can be different, but there is no reason to complicate things.
type
accepts a dictionary to define the attributes of the class. So:Can be translated to:
And used as a normal class:
And of course, you can inherit from it, so:
would be:
Eventually you'll want to add methods to your class. Just define a function with the proper signature and assign it as an attribute.
And you can add even more methods after you dynamically create the class, just like adding methods to a normally created class object.
You see where we are going: in Python, classes are objects, and you can create a class on the fly, dynamically.
This is what Python does when you use the keyword
class
, and it does so by using a metaclass.What are metaclasses (finally)
Metaclasses are the 'stuff' that creates classes.
You define classes in order to create objects, right?
But we learned that Python classes are objects.
Well, metaclasses are what create these objects. They are the classes' classes, you can picture them this way:
You've seen that
type
lets you do something like this:It's because the function
type
is in fact a metaclass.type
is the metaclass Python uses to create all classes behind the scenes.Now you wonder why the heck is it written in lowercase, and not
Type
?Well, I guess it's a matter of consistency with
str
, the class that creates strings objects, andint
the class that creates integer objects.type
is just the class that creates class objects.You see that by checking the
__class__
attribute.Everything, and I mean everything, is an object in Python. That includes ints, strings, functions and classes. All of them are objects. And all of them have been created from a class:
Now, what is the
__class__
of any__class__
?So, a metaclass is just the stuff that creates class objects.
You can call it a 'class factory' if you wish.
type
is the built-in metaclass Python uses, but of course, you can create your own metaclass.The
__metaclass__
attributeIn Python 2, you can add a
__metaclass__
attribute when you write a class (see next section for the Python 3 syntax):If you do so, Python will use the metaclass to create the class
Foo
.Careful, it's tricky.
You write
class Foo(object)
first, but the class objectFoo
is not created in memory yet.Python will look for
__metaclass__
in the class definition. If it finds it, it will use it to create the object classFoo
. If it doesn't, it will usetype
to create the class.Read that several times.
When you do:
Python does the following:
Is there a
__metaclass__
attribute inFoo
?If yes, create in memory a class object (I said a class object, stay with me here), with the name
Foo
by using what is in__metaclass__
.If Python can't find
__metaclass__
, it will look for a__metaclass__
at the MODULE level, and try to do the same (but only for classes that don't inherit anything, basically old-style classes).Then if it can't find any
__metaclass__
at all, it will use theBar
's (the first parent) own metaclass (which might be the defaulttype
) to create the class object.Be careful here that the
__metaclass__
attribute will not be inherited, the metaclass of the parent (Bar.__class__
) will be. IfBar
used a__metaclass__
attribute that createdBar
withtype()
(and nottype.__new__()
), the subclasses will not inherit that behavior.Now the big question is, what can you put in
__metaclass__
?The answer is: something that can create a class.
And what can create a class?
type
, or anything that subclasses or uses it.Metaclasses in Python 3
The syntax to set the metaclass has been changed in Python 3:
i.e. the
__metaclass__
attribute is no longer used, in favor of a keyword argument in the list of base classes.The behaviour of metaclasses however stays largely the same.
Custom metaclasses
The main purpose of a metaclass is to change the class automatically, when it's created.
You usually do this for APIs, where you want to create classes matching the current context.
Imagine a stupid example, where you decide that all classes in your module should have their attributes written in uppercase. There are several ways to do this, but one way is to set
__metaclass__
at the module level.This way, all classes of this module will be created using this metaclass, and we just have to tell the metaclass to turn all attributes to uppercase.
Luckily,
__metaclass__
can actually be any callable, it doesn't need to be a formal class (I know, something with 'class' in its name doesn't need to be a class, go figure... but it's helpful).So we will start with a simple example, by using a function.
Now, let's do exactly the same, but using a real class for a metaclass:
But this is not really OOP. We call
type
directly and we don't override or call the parent__new__
. Let's do it:You may have noticed the extra argument
upperattr_metaclass
. There is nothing special about it:__new__
always receives the class it's defined in, as first parameter. Just like you haveself
for ordinary methods which receive the instance as first parameter, or the defining class for class methods.Of course, the names I used here are long for the sake of clarity, but like for
self
, all the arguments have conventional names. So a real production metaclass would look like this:We can make it even cleaner by using
super
, which will ease inheritance (because yes, you can have metaclasses, inheriting from metaclasses, inheriting from type):That's it. There is really nothing more about metaclasses.
The reason behind the complexity of the code using metaclasses is not because of metaclasses, it's because you usually use metaclasses to do twisted stuff relying on introspection, manipulating inheritance, vars such as
__dict__
, etc.Indeed, metaclasses are especially useful to do black magic, and therefore complicated stuff. But by themselves, they are simple:
Why would you use metaclasses classes instead of functions?
Since
__metaclass__
can accept any callable, why would you use a class since it's obviously more complicated?There are several reasons to do so:
UpperAttrMetaclass(type)
, you know what's going to follow__new__
,__init__
and__call__
. Which will allow you to do different stuff. Even if usually you can do it all in__new__
, some people are just more comfortable using__init__
.Why would you use metaclasses?
Now the big question. Why would you use some obscure error prone feature?
Well, usually you don't:
Python Guru Tim Peters
The main use case for a metaclass is creating an API. A typical example of this is the Django ORM.
It allows you to define something like this:
But if you do this:
It won't return an
IntegerField
object. It will return anint
, and can even take it directly from the database.This is possible because
models.Model
defines__metaclass__
and it uses some magic that will turn thePerson
you just defined with simple statements into a complex hook to a database field.Django makes something complex look simple by exposing a simple API and using metaclasses, recreating code from this API to do the real job behind the scenes.
The last word
First, you know that classes are objects that can create instances.
Well in fact, classes are themselves instances. Of metaclasses.
Everything is an object in Python, and they are all either instances of classes or instances of metaclasses.
Except for
type
.type
is actually its own metaclass. This is not something you could reproduce in pure Python, and is done by cheating a little bit at the implementation level.Secondly, metaclasses are complicated. You may not want to use them for very simple class alterations. You can change classes by using two different techniques:
99% of the time you need class alteration, you are better off using these.
But 98% of the time, you don't need class alteration at all.
Note, this answer is for Python 2.x as it was written in 2008, metaclasses are slightly different in 3.x, see the comments.
Metaclasses are the secret sauce that make 'class' work. The default metaclass for a new style object is called 'type'.
Metaclasses take 3 args. 'name', 'bases' and 'dict'
Here is where the secret starts. Look for where name, bases and the dict come from in this example class definition.
Lets define a metaclass that will demonstrate how 'class:' calls it.
And now, an example that actually means something, this will automatically make the variables in the list "attributes" set on the class, and set to None.
Note that the magic behaviour that 'Initalised' gains by having the metaclass
init_attributes
is not passed onto a subclass of Initalised.Here is an even more concrete example, showing how you can subclass 'type' to make a metaclass that performs an action when the class is created. This is quite tricky:
The type() function can return the type of an object or create a new type,
for example, we can create a Hi class with the type() function and do not need to use this way with class Hi(object):
In addition to using type() to create classes dynamically, you can control creation behavior of class and use metaclass.
According to the Python object model, the class is the object, so the class must be an instance of another certain class. By default, a Python class is instance of the type class. That is, type is metaclass of most of the built-in classes and metaclass of user-defined classes.
Magic will take effect when we passed keyword arguments in metaclass, it indicates the Python interpreter to create the CustomList through ListMetaclass. new (), at this point, we can modify the class definition, for example, and add a new method and then return the revised definition.