In the previous lesson, I showed you how to dynamically create a class using the
type() function and how to make an object callable with
.__call__(). In this lesson, I’ll finally be getting to the subject matter: metaclasses.
You saw how the
.__call__() method can turn an object into a callable. At a more general level, you can state that in Python, parenthesis invokes objects, and, well, you may have heard somewhere that everything’s an object, right?
So classes are objects, and when you use parenthesis on the class, you are invoking it. It’s callable. All classes inherent from the base
type metaclass, and that metaclass has a
That’s why the
.__call__() tangent in the previous lesson. It’s all fitting together. So what happens when you invoke a class? The
type metaclass creates the object and calls two dunder methods on it:
There are default implementations of these methods, so if you don’t define them, the parent’s methods get called.
.__new__() is responsible for creating the object, which I’ll show you in a minute.
.__init__() you’re probably familiar with. This is why you’ll occasionally come across someone making the argument that the
.__init__() method is not a constructor, so you shouldn’t call it that.
01:35 In most languages, the constructor creates and initializes the object. In Python, these things are separated into two parts. Don’t let that stop you from calling it a constructor.
01:46 Everyone knows what you mean. And if someone challenges you, you can go to town on them about types and callables because you just learned all that. All right, let’s go play with some of these special methods.
I’m going to use the same technique as before, creating a function then changing it into a method. I’ll start with the
The primary purpose of
new() is to create the object. You can do that by using the
.__new__() method on the
obj object. Yep, type of
type isn’t the only thing. You’ve also got the
02:24 You pass in a reference to the class to be constructed, And Python instantiates an instance for you.
Once you’ve got the instance, you can add attributes to it. And the one thing you must remember is your
.__new__() must return the constructed instance. With the function defined, let’s use
type() to create a class.
In its attribute dictionary, I’m turning the
new() function into the
.__new__() method. Now I’m going to create some
ceviche. Mm, raw seafood.
.__new__() was called when the instance was created, the
.description was added as an attribute. Of course, this is an overly complicated way of doing this.
You can simply override
.__new__() in your class syntax definition, just like you override
.__init__(). But then you wouldn’t be learning what I’m going to show you next.
You just saw how
.__new__() is called when an object instance is created. But what if you want something done when the class itself is created?
type metaclass does have a
.__new__() method, but you’re not allowed to override it. There’d be no way to instantiate, and ultimately the buck has to stop somewhere.
So instead, Python provides the
metaclass argument to the inheritance syntax. You create a metaclass like a regular class, except it inherits from
type and typically overrides the
.__new__() on the class.
04:13 The resulting metaclass can then be passed into the inheritance part of the class definition. This is done inside the parenthesis where you indicate inheritance from another class.
You provide the
metaclass keyword argument and pass it the metaclass to be used as a hook into creation. Let’s go turn all that word soup into a concrete example.
04:36 The metaclass I’m going to show you contains a simple counter that gets incremented when it is used. This is kind of like having a counter that tracks how many instances of an object there are. But instead, this counts how many classes inherit using this metaclass.
The first step for a metaclass is to define a class that inherits from
type. I almost always use the word meta in the name of the class to help provide some clarity.
05:06 The other technique I’ve seen, and you’ll see later, is to use the word type in the name instead. In order to be able to count things, I need a counter, so here I’ve created an attribute on the metaclass.
Pardon the lack of spacing here. The REPL doesn’t like blank lines. I’m overriding the
.__new__() method of the metaclass. Note the arguments here.
Do they look familiar? They’re the same three arguments that the
type() function uses to create a class
.__new__(). I’m using
super() to invoke the class creation of the parent, which will be the new
05:58 This is what actually creates the class instance. This will be the instance of the class using the metaclass, not the metaclass itself.
And here is my side effect. Each time a new class is created using this metaclass counter, the metaclass
.count attribute is incremented. I initialized it to
0, so the first creation will have a count of
Finally, like an object’s
.__new__(), the class’s
.__new__() needs to return the created class. Let’s use the metaclass by including it in the inheritance syntax.
This defines the
Pie class—mmmm, pie—and uses the counter metaclass by passing in a reference with the
metaclass argument. Note that it’s a reference to
metaclass. It isn’t invoked.
06:58 There are no parentheses here. I prefer simple pie. This one’s more of a crust. There’s nothing inside of it. Make some apple … and some raspberry … and let’s see the count.
Did you expect that to work? Remember the
.count attribute is on the metaclass, not on the class. It isn’t a class attribute. I can get at it, though, using the
.__class__ attribute. The count is
raspberry isn’t the relevant part. This code doesn’t care how many instances of
Pie there are, only how many classes use the metaclass counter. So far, there’s only one
Pie, so the count is
1. What’s better than pie?
Cake, of course. Cake is definitely better. I’ll fight you. Like
Pie, here I’m using the
metaclass keyword to use the counter metaclass hook.
Pie, I’m going to keep it simple. Some cake, some more cake.
And there’s the counter. Four object instances, but only two classes, so the count is
2. I’m not sure why you’d want to count how many classes are based on a particular metaclass, but the important part is the side effect. You can now hook when a programmer defines a class and do things with it. What kinds of things? Well, I’m glad you asked. In the next lesson, I’ll outline some common uses of metaclasses in the real world.
Become a Member to join the conversation.