Pay attention to the “water drop and silver bullet” public account, the first time to obtain high-quality technical dry goods. 7 years of senior back-end development, with a simple way to explain the technology clearly.

This article takes about 10 minutes to read.

If you’ve looked at any of the better Python open source frameworks, you’ve probably seen metaclasses. For example, the class attribute __metaclass__ is defined in aclass, which indicates that the class is created using metaclass.

How exactly does the metaclass implementation work? What kind of problems can using metaclasses help us solve in development?

In this article, we’ll take a look at the context of Python metaclasses.

What is a metaclass?

We all know that defining a class and calling its constructor initializes an instance like this:

class Person(object)

    def __init__(name) :
        self.name = name

p = Person('zhangsan')
Copy the code

Have you ever wondered how the class that we normally define is created?

Don’t worry, let’s look at an example:

>>> a = 1               A is an instance of int
>>> a.__class__
<type 'int'>

>>> b = 'abc'           B is an instance of STR
>>> b.__class__
<type 'str'>

>>> def c() :            C is an instance of function
.    pass
>>> c.__class__
<type 'function'>

>>> class D(object) :    D is an instance of d
.    pass
>>> d.__class__
<class '__main__.D'>
Copy the code

In this example, we define int, STR, function, and class, and then call their __class__ method, respectively. This __class__ method returns how the instance was created.

From the result returned by the method we can see:

  • Create an integeraThe class isintThat is to sayaintAn instance of
  • Creating a stringbThe class isstrThat is to saybstrAn instance of
  • Create a functioncThe class isfunctionThat is to saycfunctionAn instance of
  • Create an instancedThe class isclassThat is to saydclassAn instance of

In addition to this, we use similar things in development such as lists and dict, so you can test it out and see the results.

Now that we know that the classes that create these instances are int, STR, function, and class, let’s think a little bit more about how these classes are created.

Similarly, we call the __class__ methods of these classes and observe the result:

>>> a = 1
>>> a.__class__.__class__
<type 'type'>
>>>
>>> b = 'abc'
>>> b.__class__.__class__
<type 'type'>
>>>
>>> def c() :
.    pass
>>> c.__class__.__class__
<type 'type'>
>>>
>>> class D(object) :
.    pass
>>> d = D()
>>> d.__class__.__class__
<type 'type'>
Copy the code

As you can see from the result, the classes that create these classes are all types, so type is the metaclass that creates all the classes. That is, metaclasses are used to create classes.

You can understand it this way:

  1. The metaclass – > class
  2. Class – > instance

In pseudocode, it looks like this:

klass = MetaClass()     # metaclass creates class
obj = klass()           Class create instance
Copy the code

Isn’t that interesting?

Here, too, you can get a sense of what this means: Everything in Python is an object!

Common types, methods, instances, and classes can all be viewed as objects, and their origins are metaclasses.

In Python, we can create a class using the type method. The syntax for the type method is as follows:

type(class_name, (base_class, ...) , {attr_key: attr_value, ... })Copy the code

For example, we create MyClass using the type method and let it inherit object like this:

>>> A = type('MyClass', (object), {})# type Creates a class that extends from Object
>>> A
<class '__main__.MyClass'> > > >A()"__main__.MyClass object at 0x10d905950>
Copy the code

We can also use type to create a class containing properties and methods:

>>> def foo(self) :
.    return 'foo'.>>> name = 'zhangsan'
>>>
Class B inherits object containing the name property and the foo method
>>> B = type('MyClass', (object), {'name': name, 'foo': foo}) 
>>> B.name          # Print the name property
'zhangsan'
>>> print B().foo() Call the foo method
foo
Copy the code

A class created using the Type method is no different from a class we defined ourselves.

In addition to creating aclass using the type method, we can also create aclass using the class attribute __metaclass__, which is the “custom metaclass” described below.

Custom metaclasses

We can use the class attribute __metaclass__ to transfer the creation of aclass elsewhere, like this:

class A(object) :
    __metaclass__ = ... The creation of this class is handed over elsewhere
    pass
Copy the code

In this example, we define class A first, and then define A class attribute __metaclass__, which represents the process of creating class A, to be handled elsewhere.

So what about the class attribute __metaclass__?

It can be a method or a class.

Create classes using methods

If the class attribute __metaclass__ is assigned to a method, the process of creating the class is assigned to a method.

def create_class(name, bases, attr) :
    print 'create class by method... '
    Create a class using type without doing anything
    return type(name, bases, attr)

class A(object) :
    The process of creating a class is handed to a method
    __metaclass__ = create_class

# Output:    
# create class by method ...
Copy the code

We define the create_class method and assign it to __metaclass__ so that when class A is created, the create_class method will be called.

The logic in create_class, as we saw above, uses the type method to create a class and return it.

Create classes from classes

Now that you know how to create a class with a method, let’s look at creating another class with a class.

class B(type) :
    The __new__ method must be defined to return a class
    def __new__(cls, name, bases, attr) :
        print 'create class by B ... '
        return type(name, bases, attr)

class A(object) :
    The process of creating the class is handed over to B
    __metaclass__ = B
    
# Output:
# create class by B ...
Copy the code

In this example, we define class B and assign it to A’s class variable __metaclass__, which means that the process of creating A is handed over to class B.

B first inherits type, then defines the __new__ method, and finally calls the type method to return A class, so that when class A is created, the __new__ method of class B is automatically called, and then an instance of the class is obtained.

The process of creating a class

Ok, so above we demonstrated two ways to create a class from metaclass, method creation and class creation.

The complete process for creating a class is as follows:

  1. Check if there are any in the class__metaclass__Property, if any, is called__metaclass__Specified method or class creation
  2. If not in the class__metaclass__Property, then the search continues in the parent class
  3. If none exists in any parent class, usetypeCreate this class

That is, if we do not specify __metaclass__, then all classes are created by type by default, which is how most of us define classes.

If __metaclass__ is specified in aclass, the creation of the class is handed over to an external entity that can define the specific creation logic.

Which is the better way to create a class?

Although there are two ways to create classes, which one is better?

In general, we recommend using the class method to create, which has the following advantages:

  • Use classes to express intent more clearly
  • Using classes is more OOP because classes can inherit from other classes and are more user-friendly with object-oriented features
  • Use classes to better organize your code structure

In addition, when creating a class using a class, there is an optimization point: it is not recommended to call type directly in the __new__ method. Instead, it is recommended to call super’s __new__ to create the class, and the result is the same as the type method:

class B(type) :

    def __new__(cls, name, bases, attr) :
        # create class with super.__new__
        return super(B, cls).__new__(cls, name, bases, attr)    
Copy the code

Custom behavior when creating a class

When we created a class with metaclasses earlier, its functionality was very simple. Now let’s look at how you can define some of your own logic when creating a class using a metaclass, and then change the properties or behavior of the class.

Let’s look at this example:

# coding: utf8

class Meta(type) :
    def __new__(cls, name, bases, attr) :
        Class attributes created with Meta will be capitalized
        for k, v in attr.items():
            if not k.startswith('__'):
                attr[k] = v.upper()
            else:
                attr[k] = v
        return type(name, bases, attr)

class A(object) :
    Create a class from Meta
    __metaclass__ = Meta

    name = 'zhangsan'

class B(object) :
    Create a class from Meta
    __metaclass__ = Meta

    name = 'lisi'

The print class attribute is automatically capitalized
print A.name    # ZHANGSAN
print B.name    # LISI
Copy the code

In this example, we define A metaclass, Meta, and then hand over the creation process to Meta when defining classes A and B. In the Meta class, we can take the attributes of A and B and convert them to uppercase.

So when we print the properties of A and B, the variables are lowercase, but the output is uppercase, and that’s where the metaclass comes in.

Usage scenarios

Now that you know how metaclasses work, what scenarios are metaclasses used in?

Djangos ORM, PeeWee, djangos ORM, Djangos ORM, Djangos ORM, Djangos ORM, Djangos ORM

class Person(models.Model) :
    # Note: Name and age are class attributes
    name = models.CharField(max_length=30)
    age = models.IntegerField()
    
person = Person(name='zhangsan', age=20)
print person.name   # zhangsan
print person.age    # 20
Copy the code

In this code, we define a Person class, and then we define the class attributes name and Age, which are of type CharField and IntegerField, respectively, and then we initialize the Person instance, It then gets the name and age attributes from the instance, and outputs STR and int instead of CharField and IntegerField.

The secret is that when the Person class is created, its logic is handed over to another class, which transforms the attributes of the class into a mapping of objects to the table, allowing friendly access to the corresponding field values in the table through instance attributes.

conclusion

To sum up, this article looked at how metaclasses are implemented, and learned that metaclasses are the root of all classes. We can externalize the process of creating aclass by using the Type method, or by defining __metaclass__ in the class.

When creating aclass using __metaclass__, it can be either a method or aclass. We usually implement a metaclass as a class, which makes it easier to organize our code for object-oriented implementation.

When use metaclasses to create a class, we can modify the details of creating a class, such as unified transformation for attributes, or add new method, and so on, this for us to develop a complex function of the class are very friendly, it can create the class details of shielding in the metaclass, so metaclass is often used in the frame of the excellent open source.

My advanced Python series:

  • Python Advanced – How to implement a decorator?
  • Python Advanced – How to use magic methods correctly? (on)
  • Python Advanced – How to use magic methods correctly? (below)
  • Python Advanced — What is a metaclass?
  • Python Advanced – What is a Context manager?
  • Python Advancements — What is an iterator?
  • Python Advancements — How to use yield correctly?
  • Python Advanced – What is a descriptor?
  • Python Advancements – Why does GIL make multithreading so useless?

Crawler series:

  • How to build a crawler proxy service?
  • How to build a universal vertical crawler platform?
  • Scrapy source code analysis (a) architecture overview
  • Scrapy source code analysis (two) how to run Scrapy?
  • Scrapy source code analysis (three) what are the core components of Scrapy?
  • Scrapy source code analysis (four) how to complete the scraping task?

Want to read more hardcore technology articles? Focus on”Water drops and silver bullets”Public number, the first time to obtain high-quality technical dry goods. 7 years of senior back-end development, with a simple way to explain the technology clearly.