This is the fifth day of my participation in Gwen Challenge

Wechat public number search [program yuan xiaozhuang], pay attention to the halfway program yuan how to rely on Python development to support the family ~

preface

Everything in Python is an object. Classes defined by the class keyword are also objects. Objects are instantiated by classes. Let’s come to Kangkang

Metaclasses is introduced

All objects are instantiated or obtained by calling a class. In Python, everything is an object. A class defined by the class keyword is essentially an object, and an object is obtained by calling a class.

A metaclass is a class that is used to instantiate the resulting class, so we can get the following relationship:

How do I view Python’s built-in metaclasses? We can use the type method:

>>> class Test() :
.    pass.>>> type(Test)  View the type of the custom class
<class 'type'> > > >type(int) # viewpythonType of built-in type <class 'type'>
Copy the code

All the above code results in

. Type is the built-in metaclass. All classes defined by the class keyword and the built-in classes are instantiated by the metaclass type.

type & object

Since there is no distinction between classical and modern classes in Python3, object is the base class for all classes in Python3. What is the built-in metaclass Type? ** Type is the type of the object class. ** Type is the type of the object class. Object is the base of type. This problem is a bit like the chicken or the egg, we can briefly analyze it in code (because small farms are limited…). :

>>> object.__class__
<class 'type'> > > >type.__class__
<class 'type'> > > >object.__bases__() > > >type.__bases__
(<class 'object'>,) > > >type(object)
<class 'type'> > > >type(type)
<class 'type'>
Copy the code

In python3 classes and types are the same thing, so object.__class__ and type(object) get the same result. The base class of object is empty, but the base class of type is object, but the type of object is type.

Object and Type are two source objects in Python. If you are interested, you can find the answer in the official Python documentation.

The class keyword is used to create a class

When class is created, the metaclass must be called. What arguments do we need to pass in to call type? Are the three components of a class, which are:

1, the class name, such as class_name = Test

2. The parent (base) of a class, as class_bases = (object,)

Class namespace class_dict. The class namespace is obtained when executing the class body code

These three arguments are passed in turn when type is called. The exec command can be executed as a Python function and can take three arguments:

Argument one, which contains a string of python syntactic code;

Second, the name in the global namespace in dictionary form and its corresponding value.

Parameter 3, the name in the dictionary local namespace and its corresponding value;

# python string
strs = '' global name, age # global name = 'python' age = 18 addr = 'xx' # local name ''

Define the name and value of the global scope
globals = {
   'a': 1.'b': 2
}

Define the name and value of the local scope
locals = {
    'x': 3.'y':4
}

exec(strs, globals.locals)

>>> globals
{'a': 1.'b': 2. .'name': 'python'.'age': 18}

>>> locals
{'x': 3.'y': 4.'addr': 'xx'}
Copy the code

Now that we know what exec does, we can examine how the class keyword generates the steps of the class with the help of the Type metaclass:

# 1 Define the class name
class_name = 'Test'

# 2 Define the base class (parent class) of the class
class_bases = (object.)Execute the class body code to get the namespace of the class
class_dic = {}

# 4 Define class body code (essentially a string)
class_body = """ def __init__(self,name,age): self.name=name self.age=age def test(self): print('%s:%s' %(self.name,self.name)) """

# 5 Convert a string to a syntax that Python recognizes: store the name generated by the class_body runtime in class_DIC
exec(class_body,{},class_dic)
View the class namespace
print(class_dic)

Call the metaclass to produce the class
Test = type(class_name, class_bases, class_dic)

Call the class to produce the object
t = Test('python'.'12')
Copy the code

Custom metaclasses control the creation of classes

The generation of metaclasses can be controlled by controlling the steps used to call metaclasses. The call of metaclasses is the same as the call of custom ordinary classes. So, let’s take a look at what happens when a class is called.

Define a class
class Test() :
    
    def __init__(self) :
        self.name = 'python'
        
    def test(self) :
        print(self.name)
        
Class instantiation produces objects
t = Test()
Copy the code

When instantiating an object, the class automatically calls its __init__ method to add attributes to the empty object. So there must be other methods called before the __init__ method is called.

There is no distinction between classical and modern classes in Python3. The custom ordinary classes in Python3 inherit the object class by default, so when the class is called to produce an object, the method before the __init__ method is run generates an empty object and returns it, passing the empty object as an argument to the __init__ method. This method is the __new__ method.

When a class instantiates an object in parentheses, the __new__ method is first executed, which runs before the __init__ method and returns an empty object. If the method does not return a value, the __init__ method is not called.

Knowing the steps of class invocation, you can customize the metaclass to control class generation.

Custom metaclasses

A class does not specify its own metaclass. The default metaclass of this class is type. Instead of using the built-in metaclass type, we can customize the metaclass by inheriting the type class and specifying the name of the custom metaclass when creating the ordinary class.

# Custom metaclasses
class MyMeta(type) :  A custom metaclass must inherit type, otherwise it is a normal class
    
    An empty object must be returned before the __init__ method is executed. Since this method is the first method to run after the class is called, no object is generated. So the first argument to this method must be the class itself (MyMeta),*args, and **kwargs to receive the arguments needed to call the metaclass to produce the object (the class name of the class's base class namespace).
    def __new__(* * kwargs CLS, * args) :
        return type.__new__(cls, *args, **kwargs)  Call the __new__ method of the parent class type directly
    
    Class generation is controlled by __init__. Calling a custom metaclass is the same as calling the built-in metaclass type, passing in the class name, the parent classes of the class, and the namespace of the class body code. The __init__ method takes the first argument from an empty object generated by the __new__ method. ' ' '
    def __init(self, class_name, class_bases, class_dict) :
        
        Within this method you can control the generation of classes.
        if not class_name.istitle():  The first letter of the implementation class must be capitalized, otherwise an exception will be thrown
    		raise NameError('Class name must be capitalized')
        
        if '__doc__' not in class_dict or len(class_dict['__doc__'].strip())==0:  The implementation class must be documented, otherwise it will throw an exception
            raise TypeError('Must be documented')
            
Copy the code

Custom metaclasses control the creation of classes

First, use the class keyword to create a class without the class keyword.

Next, let’s look at creating a class using the custom metaclass control class keyword. You can try to verify how a metaclass controls class creation without documenting and capitalizing the class name:

class Test(metaclass=MyMeta) :
    
    I'm a document comment.
    
    def __init__(self) :
        self.name = 'python'
        
    def test(self) :
        print('test')
Copy the code

From the above code we can draw the following conclusions:

Whenever a class is called, its __new__ and __init__ methods are called in turn;

The __new__ method returns an empty object, just like a blank sheet of paper;

__init__ takes the white paper produced in the __new__ method and draws different patterns on it.

Custom metaclasses control the invocation of classes

Why can a class be called with parentheses? How do we ensure that the __new__ method of the class is run before the __init__ method of the class?

The answer to the above question is the __call__ method defined in the class. If you want an object to become a callable (called in parentheses), you need to define a __call__ method in the class of the object. The return value of the call to the callable is the return value of the __call__ method.

class Test() :
    
    def __init__(self) :
        self.name = 'python'
    
    def __call__(self, *args, **kwargs) :  # self is an object of class Test
        print(self)  # <__main__.Test object at 0x000001C78CE78FD0>
        print(self.name)
        
t = Test()
t()  # python
Copy the code

Therefore, we can draw the following conclusions:

Object parenthesized calls call the __call__ method defined in the object’s class;

Class parenthesized calls call __call__ methods in either the built-in metaclass or custom metaclass, depending on what the class’s metaclass is;

A custom metaclass parenthesized call to the __call__ method in the built-in metaclass.

We can simply test the above conclusion:

class MyMeta(type) :
	
    def __call__(self, *args, **kwargs) : 
        print(self)
        print(args)
        print(kwargs)
        
        return 'test'

class Test(metaclass=MyMeta) :
    
    def __init__(self, name, age) :
        self.name = name
        self.age = age

Call Test
t = Test()
print(t)

'''
<class '__main__.Test'>
('haha', '123')
{}
test
'''
Copy the code

From the above code we can infer that when Test is called, the __call__ method in the custom metaclass is called and the return value is assigned to the object produced by the calling class.

We can also control the invocation of a class by customizing a metaclass with a __call__ method, that is, generating objects.

class Mymeta(type) :

    def __init__(self, class_name, class_bases, class_dict) :
        
        The first letter of the implementation class must be capitalized, otherwise an exception will be thrown
        if not class_name.istitle():
            raise NameError('Class name must be capitalized')
        Classes created by the implementation must be documented, otherwise an exception will be thrown
        if '__doc__' not in class_dict or len(class_dict['__doc__'].strip())==0:
            raise TypeError('Must be documented')

    def __new__(cls, *args, **kwargs) :

        return type.__new__(cls, *args, **kwargs)

    def __call__(self, *args, **kwargs) :
        # self refers to the object generated by the current class
        people_obj = self.__new__(self)
        self.__init__(people_obj,*args, **kwargs)
        return people_obj
    
Test = MyMeta(class_name, class_bases, class_dic)  # calls the __call__ method of the built-in metaclass type

class Test(metaclass=Mymeta) :
    """ I am a document comment """
    def __new__(cls, *args, **kwargs) :
        # create empty object -- real object, real object
        return object.__new__(cls)  There is no problem with using type here
    
    def __init__(self,name) :
        self.name = name
   
    
t = Test()  The __call__ method in the custom metaclass is called
Copy the code

When a class is created from a custom metaclass, the __call__ method of type is called, which does three things internally:

What type.__call__ does:

1. Call the __new__ method in the custom metaclass to produce an empty object

2. Call the __init__ method in the custom metaclass to add unique attributes to the empty object

3, Return an initialized custom metaclass, which is the Test class above

Calling the Test class calls the custom metaclass myMeta.__call__ method, which also does three things:

MyMeta.__call__ does:

1. Call the __new__ method of the Test class to produce an empty object

2. Call the __init__ method of the Test class to add unique attributes to the empty object

3. Return an initialized Test object and assign it to t

conclusion

The code word is not easy, reprint please explain the source, pass by the little friends of the lovely little finger point like and then go (╹▽╹)