When discussing metaclasses, the docs state:
You can of course also override other class methods (or add new methods); for example defining a custom
__call__()
method in the metaclass allows custom behavior when the class is called, e.g. not always creating a new instance.
My questions is: suppose I want to have custom behavior when the class is called, for example caching instead of creating fresh objects. I can do this by overriding the __new__
method of the class. When would I want to define a metaclass with __call__
instead? What does this approach give that isn't achievable with __new__
?
The subtle differences become a bit more visible when you carefully observe the execution order of these methods.
Note that the code above doesn't actually do anything other than log what we're doing. Each method defers to its parent implementation i.e. its default. So beside logging it's effectively as if you had simply declared things as follows:
And now let's create an instance of
Class_1
Therefore if
type
is the parent ofMeta_1
we can imagine a pseudo implementation oftype.__call__()
as such:Notice from the call order above that
Meta_1.__call__()
(or in this casetype.__call__()
) is given the opportunity to influence whether or not calls toClass_1.__new__()
andClass_1.__init__()
are eventually made. Over the course of its executionMeta_1.__call__()
could return an object that hasn't even been touched by either. Take for example this approach to the singleton pattern:Let's observe what happens when repeatedly trying to create an object of type
Class_2
Now observe this implementation using a class'
__new__()
method to try to accomplish the same thing.Notice that the above implementation even though successfully registering a singleton on the class, does not prevent
__init__()
from being called, this happens implicitly intype.__call__()
(type
being the default metaclass if none is specified). This could lead to some undesired effects:I thought a fleshed out Python 3 version of pyroscope's answer might be handy for someone to copy, paste and hack about with (probably me, when I find myself back at this page looking it up again in 6 months). It is taken from this article:
Outputs:
Another great resource highlighted by the same article is David Beazley's PyCon 2013 Python 3 Metaprogramming tutorial.
One difference is that by defining a metaclass
__call__
method you are demanding that it gets called before any of the class's or subclasses's__new__
methods get an opportunity to be called.Notice that
SubFoo.__new__
never gets called. In contrast, if you defineFoo.__new__
without a metaclass, you allow subclasses to overrideFoo.__new__
.Of course, you could define
MetaFoo.__call__
to callcls.__new__
, but that's up to you. By refusing to do so, you can prevent subclasses from having their__new__
method called.I don't see a compelling advantage to using a metaclass here. And since "Simple is better than complex", I'd recommend using
__new__
.It's a matter of lifecycle phases and what you have access to.
__call__
gets called after__new__
and is passed the initialization parameters before they get passed on to__init__
, so you can manipulate them. Try this code and study its output:The direct answer to your question is: when you want to do more than just customize instance creation, or when you want to separate what the class does from how it's created.
See my answer to Creating a singleton in Python and the associated discussion.
There are several advantages.
It allows you to separate what the class does from the details of how it's created. The metaclass and class are each responsible for one thing.
You can write the code once in a metaclass, and use it for customizing several classes' call behavior without worrying about multiple inheritance.
Subclasses can override behavior in their
__new__
method, but__call__
on a metaclass doesn't have to even call__new__
at all.If there is setup work, you can do it in the
__new__
method of the metaclass, and it only happens once, instead of every time the class is called.There are certainly lots of cases where customizing
__new__
works just as well if you're not worried about the single responsibility principle.But there are other use cases that have to happen earlier, when the class is created, rather than when the instance is created. It's when these come in to play that a metaclass is necessary. See What are your (concrete) use-cases for metaclasses in Python? for lots of great examples.