Sep 26, 2020

Metaclasses in Python | PYTHON LANGUAGE | Coding Winds

                        Metaclasses

Hey guys, we will here learn about metaclasses in python.

The term metaprogramming refers to the potential for a program to have knowledge of or manipulate itself. Python supports a form of metaprogramming for classes called metaclasses.

Metaclasses are an esoteric OOP concept, lurking behind virtually all Python code. You are using them whether you are aware of it or not. For the most part, you don’t need to be aware of it. Most Python programmers rarely, if ever, have to think about metaclasses.

 

“Metaclasses are deeper magic than 99% of users should ever worry about. If you wonder whether you need them, you don’t (the people who actually need them know with certainty that they need them, and don’t need an explanation about why).”                 

- Tim Peters

 

There are some python programmers who believe you should never use custom metaclasses. They think that custom metaclasses aren’t necessary.

Still, understanding Python metaclasses is worthwhile, because it leads to a better understanding of the internals of Python classes in general. You never know, you may one day find yourself in one of those situations where you just know that a custom metaclasses is what you want.

Before proceeding further we should know about old-style and new-style classes.

Old-style classes

With old-style classes, class and type are not quite the same thing.

 An instance of an old-style class is always implemented from a single built-in type called instance. If obj is an instance of an old-style class, obj.__class__ designates the class, but type (obj) is always instance.

Let’s look at the example in python 2.7,

 


New-style classes

New-style classes unify the concepts of class and type. If obj is an instance of a new-style class, type(obj) is the same as obj.__class__ (in python3):

Note: - The type of x is class Foo, as you would expect. But the type of Foo, the class itself, is type. In general, the type of any new-style class is type.

The type of the built-in classes you are familiar with is also type, i.e.  int float, dict, list, tuple

We know why type()  is used for, determining the type of object used.

Example, 


You can also call type() with three arguments—type(<name>, <bases>, <dct>):

  • <name> specifies the class name. This becomes the __name__ attribute of the class.
  • <bases> specifies a tuple of the base classes from which the class inherits. This becomes the __bases__ attribute of the class.
  • <dct> specifies a namespace dictionary containing definitions for the class body. This becomes the __dict__ attribute of the class.

Calling type() in this manner creates a new instance of the type metaclass. In other words, it dynamically creates a new class.



Custom Metaclasses

Look at the example below,

The expression Foo() creates a new instance of class Foo. When the interpreter encounters Foo(), the following occurs:

  • The __call__() method of Foo’s parent class is called. Since Foo is a standard new-style class, its parent class is the type metaclass, so type’s __call__() method is invoked.
  • That __call__() method in turn invokes the following:
    • __new__()
    • __init__()

If Foo does not define __new__() and __init__(), default methods are inherited from Foo’s ancestry. But if Foo does define these methods, they override those from the ancestry, which allows for customized behaviour when instantiating Foo.

In the following, a custom method called new() is defined and assigned as the __new__() method for Foo:


This modifies the instantiation behaviour of class Foo: each time an instance of Foo is created, by default it is initialized with an attribute called attr, which has a value of 100. (Code like this would more usually appear in the __init__() method and not typically in __new__(). This example is contrived for demonstration purposes.)