metaclasses and the
type keyword are each examples of little used (and,
thus, not well understood by most) Python constructs. In this article, we'll
explore the different, erm, "types" of
type() and how the Little-known use of
type relates to
Are You My Type?
The first use of
type() is the most widely known and used: to determine the
type of an object. Here, Python novices commonly interrupt and say, "But I
thought Python didn't have types!" On the contrary, everything in Python has a
type (even the types!) because everything is an object. Let's look at a few examples:
1 2 3 4 5 6 7 8
The type of
Everything is as expected, until we check the type of
What is that? Well, odd, but let's continue:
1 2 3 4 5
<class 'type'> again. Apparently the type of all classes themselves is
type (regardless of if they're built-in or user-defined). What about the type
Well, it had to end somewhere.
type is the type of all types, including
itself. In actuality,
type is a
metaclass, or "a thing that builds
classes". Classes, like
list(), build instances of that class, as
my_list = list(). In the same way,
metaclasses build types, like
Roll Your Own Metaclass
Just like regular classes,
metaclasses can be user-defined. To use it, you set
__metaclass__ attribute to the
metaclass you built. A
can be any
callable, as long as it returns a type. Usually, you'll assign a
__metaclass__ to a function that, at some point, uses a variant of
we've not yet discussed: the three parameter variety used to create classes.
The Darker Side of
As mentioned, it turns out that
type has a totally separate use, when called with three
type(name, bases, dict) creates a new type, programmatically. If
I had the following code:
We could achieve the exact same effect with the following:
Foo is now referencing a class named "Foo", whose base class is
(classes created with
type, if specified without a base class, are
automatically made new-style classes).
That's all well and good, but what if we want to add member functions to Foo? This is easily achieved by setting attributes of Foo, like so:
1 2 3 4
We could have done it all in one go with the following:
Of course, the
bases parameter is a list of base classes of
Foo. We've been
leaving it empty, but it's perfectly valid to create a new class derived from
Foo, again using
type to create it:
When Is This Ever Useful?
Once explained to someone,
metaclasses are one of those topics
where the very next question is, "OK, so when would I use it?". The answer is,
not very often at all. However, there are times when creating classes
type is the appropriate solution. Let's take a look at an
sandman is a library I wrote to automatically generate a REST API and web-based admin interface for existing databases (without requiring any boilerplate code). Much of the heavy lifting is done by SQLAlchemy, an ORM framework.
There is only one way to register a database table with SQLAlchemy: create a
Model class describing the table (not unlike Django's models). To get
SQLAlchemy to recognize a table, a class for that table must be created
in some way. Since
sandman doesn't have any advanced knowledge of the
database structure, it can't rely on pre-made model classes to register tables.
Rather, it needs to introspect the database and create these classes on the fly.
Sound familiar? Any time you're creating new classes dynamically,
the correct/only choice.
Here's the relevant code from sandman:
1 2 3 4 5 6
As you can see, if the user has not manually created a model class for a table, it
is automatically created with a
__tablename__ attribute set to the name of the
table (used by SQLAlchemy to match tables to classes).
In this article, we discussed the two uses of
metaclasses, and when
the alternate use of
type is required. Although
metaclasses are a somewhat
confusing concept, hopefully you now have a good base off of which you can build
through further study.