00:00 In the previous lesson, I showed you how to write a basic metaclass. In this lesson, I’ll be discussing some of the common real-world uses.
00:09 Before I get to some examples, first a warning. Metaclasses are pretty useful in some very specific cases, but the vast majority of code does not need this technique.
00:20 Don’t go looking for a reason to use them. Most developers aren’t familiar with this, and it can make your code seem spooky and magical. In fact, I’ll bet you’ve been using code that’s built with metaclasses and didn’t even realize it, and that’s the point.
00:33 This is a behind-the-scenes kind of mechanism.
00:40 Okay, I promised some examples. The first one is the factory pattern. Have you ever written a factory method that does some work to an object after instantiating it? Metaclasses can be used as an alternative to that.
00:52 Similarly for enforcing singletons. My favorite use is within ORMs, like Django or SQLAlchemy. The attributes in a Django ORM model define the columns of a table.
01:04 The underlying metaclass magic is inspecting the attributes you put in the class and determining what to do with the database. Meta classes are also useful if you’re trying to use alternate programming techniques, like implementing certain behaviors that aren’t native to Python.
01:19 Aspect-oriented programming is the idea of being able to make changes to a piece of code’s behavior without touching the code. For example, adding entry and exit point logging to a function without touching the function.
01:32 Decorators are in this space.
01:35 Interface-oriented programming is coding against a defined interface. This is kind of like duck-typing and is used in plugin mechanisms. Consider a text editor that knows when you’re writing a function.
01:47 It might expose hooks for doing things with that function. A plugin would only have to implement the interface and be called when the editor sees such text.
01:57 This overlaps a bit with an observer/observable pattern and often involves some sort of registration step. And of course, registration can be a side effect of either class or object instantiation.
02:22 This means that inheritance is done between objects rather than between classes and objects. Inheritance becomes based on object reuse. Using metaclasses, you could build things that behave this way. One other use of metaclasses is domain-specific languages.
02:39 These are mini-languages based on another programming language to save you the effort of having to write a parser. Let’s talk about some of these in some more detail.
02:51 The factory pattern is usually used when you need a side effect to occur on instance creation. For example, registering the object with a a central tracking data structure.
03:01 You can use a function as a factory, and this is often done with a class method on the class that you invoke instead of creating the object directly.
03:11 The factory creates the object and performs the side effect, returning the object. The problem is there’s no way to force the programmer to use the factory.
They could accidentally directly instantiate your object and skip your side effect step. With a metaclass, you instead have the metaclass cause the side effect. Of course, this means the programmer has to remember to use the metaclass in the class declaration as well. Depending on what you’re trying to do in your factory, you may be able to get by with simply overriding
.__new__() and not needing a metaclass altogether.
03:49 Singletons are where you want a single instance of an object. These are often gateways to real-world resources like references to a database connection. One use of a factory method is to enforce the single-use policy.
If the object hasn’t been created yet, the factory creates it and keeps a reference. Subsequent calls to the factory return the object. Alternatively, you can implement it as a metaclass and then throw an exception at multiple creation or track and return the object. Like with the factory, careful use of
.__new__() in the object can avoid the need for a metaclass.
04:28 If you want to make sure that your class can only ever be inherited from once, then you’d have to use a metaclass, but I’m not sure why you’d ever want to do that. I
mentioned the ORM concept earlier. So here’s a short example. This is a Django model class that I borrowed from the DRF course. The
.restricted attributes here map to varchar and Boolean columns in a database, and the whole class maps to a table.
04:57 Instances of this object correspond to rows in the table, so the metaclasses is needed to do the mapping. All the database magic has to be done at class creation, not at instance creation.
05:09 The factory and singleton examples can be accomplished without a metaclass. This ORM framework stuff requires it.
Domain-specific languages are sublanguages built in other languages, type of the
type, never mind. They’re a quick way of building a language without having to implement a whole parser. A Python-based DSL would also be Python, so the Python parser does the hard work.
05:38 Consider a DSL for constructing HTML documents. You could create classes for each tag and then have rules around what things could be inside of other things, and use metaclass hooks to enforce these kinds of rules.
05:51 And by doing these things through class declarations, you could add data to some sort of central document. The classes in your file would correspond to the HTML tags and would never get turned into actual instance objects.
06:04 The parser would then build the corresponding document as a side effect of instantiating the classes.
Well, that was a lot. One more lesson before the end. Let’s go look at how the Python standard library uses metaclasses to implement
Become a Member to join the conversation.