I want to ask what the with_metaclass()
call means in the definition of a class.
E.g.:
class Foo(with_metaclass(Cls1, Cls2)):
A metaclass in Python is a class of a class that defines how a class behaves. A class is itself an instance of a metaclass. A class in Python defines how the instance of the class will behave. In order to understand metaclasses well, one needs to have prior experience working with Python classes.
To create your own metaclass in Python you really just want to subclass type . A metaclass is most commonly used as a class-factory. When you create an object by calling the class, Python creates a new class (when it executes the 'class' statement) by calling the metaclass.
In object-oriented programming, a metaclass is a class whose instances are classes. Just as an ordinary class defines the behavior of certain objects, a metaclass defines the behavior of certain classes and their instances. Not all object-oriented programming languages support metaclasses.
In order to set metaclass of a class, we use the __metaclass__ attribute. Metaclasses are used at the time the class is defined, so setting it explicitly after the class definition has no effect.
with_metaclass()
is a utility class factory function provided by the six
library to make it easier to develop code for both Python 2 and 3.
It uses a little sleight of hand (see below) with a temporary metaclass, to attach a metaclass to a regular class in a way that's cross-compatible with both Python 2 and Python 3.
Quoting from the documentation:
Create a new class with base class base and metaclass metaclass. This is designed to be used in class declarations like this:
from six import with_metaclass class Meta(type): pass class Base(object): pass class MyClass(with_metaclass(Meta, Base)): pass
This is needed because the syntax to attach a metaclass changed between Python 2 and 3:
Python 2:
class MyClass(object): __metaclass__ = Meta
Python 3:
class MyClass(metaclass=Meta): pass
The with_metaclass()
function makes use of the fact that metaclasses are a) inherited by subclasses, and b) a metaclass can be used to generate new classes and c) when you subclass from a base class with a metaclass, creating the actual subclass object is delegated to the metaclass. It effectively creates a new, temporary base class with a temporary metaclass
metaclass that, when used to create the subclass swaps out the temporary base class and metaclass combo with the metaclass of your choice:
def with_metaclass(meta, *bases): """Create a base class with a metaclass.""" # This requires a bit of explanation: the basic idea is to make a dummy # metaclass for one level of class instantiation that replaces itself with # the actual metaclass. class metaclass(type): def __new__(cls, name, this_bases, d): return meta(name, bases, d) @classmethod def __prepare__(cls, name, this_bases): return meta.__prepare__(name, bases) return type.__new__(metaclass, 'temporary_class', (), {})
Breaking the above down:
type.__new__(metaclass, 'temporary_class', (), {})
uses the metaclass
metaclass to create a new class object named temporary_class
that is entirely empty otherwise. type.__new__(metaclass, ...)
is used instead of metaclass(...)
to avoid using the special metaclass.__new__()
implementation that is needed for the slight of hand in a next step to work.temporary_class
is used as a base class, Python first calls metaclass.__prepare__()
(passing in the derived class name, (temporary_class,)
as the this_bases
argument. The intended metaclass meta
is then used to call meta.__prepare__()
, ignoring this_bases
and passing in the bases
argument.metaclass.__prepare__()
as the base namespace for the class attributes (or just using a plain dictionary when on Python 2), Python calls metaclass.__new__()
to create the actual class. This is again passed (temporary_class,)
as the this_bases
tuple, but the code above ignores this and uses bases
instead, calling on meta(name, bases, d)
to create the new derived class.As a result, using with_metaclass()
gives you a new class object with no additional base classes:
>>> class FooMeta(type): pass ... >>> with_metaclass(FooMeta) # returns a temporary_class object <class '__main__.temporary_class'> >>> type(with_metaclass(FooMeta)) # which has a custom metaclass <class '__main__.metaclass'> >>> class Foo(with_metaclass(FooMeta)): pass ... >>> Foo.__mro__ # no extra base classes (<class '__main__.Foo'>, <type 'object'>) >>> type(Foo) # correct metaclass <class '__main__.FooMeta'>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With