Python learning 33-metaclass

Unary introduction

 

What is a metaclass? Everything comes from a sentence: everything in Python is an object. Let us first define a class and then analyze it step by step

class StanfordTeacher(object): school='Stanford' def __init__(self,name,age): self.name=name self.age=age def say(self): print('%s says welcome to the Stanford to learn Python' %self.name)

All objects are obtained by instantiating or calling the class (the process of calling the class is called class instantiation), for example, the object t1 is obtained by calling the class StanfordTeacher

t1=StanfordTeacher('lili',18) print(type(t1)) #查看对象t1的类是<class '__main__.StanfordTeacher'>

If everything is an object, then the class StanfordTeacher is essentially an object. Since all objects are obtained by calling the class, then StanfordTeacher must also be obtained by calling a class. This class is called a metaclass.

So we can deduce ===> the process of generating StanfordTeacher must have happened: StanfordTeacher = metaclass (...)

print(type(StanfordTeacher)) # 结果为<class 'type'>,证明是调用了type这个元类而产生的StanfordTeacher,即默认的元类为type

Analysis of the process of creating a class with the second class keyword

In the above, we analyzed based on the concept that everything in Python is an object: the class we define with the class keyword is itself an object, and the class responsible for generating the object is called a metaclass (metaclass can be referred to as a class of classes), The built-in metaclass is type

When the class keyword helps us create a class, it must help us call the metaclass StanfordTeacher = type (...). What are the parameters passed in when calling type? Must be a key component of a class, a class has three major components, namely

1、类名class_name='StanfordTeacher'

2. The base classes class_bases = (object,)

3. The class name space class_dic, the name space of the class is obtained by executing the class body code

When calling type, the above three parameters will be passed in sequence

In summary, the class keyword to help us create a class should be subdivided into the following four processes

 

 

 

Added: usage of exec

#exec:三个参数

#参数一:包含一系列python代码的字符串

#参数二:全局作用域(字典形式),如果不指定,默认为globals()

#参数三:局部作用域(字典形式),如果不指定,默认为locals()

#可以把exec命令的执行当成是一个函数的执行,会将执行期间产生的名字存放于局部名称空间中 g={ 'x':1, 'y':2 } l={} exec(''' global x,z x=100 z=200  m=300 ''',g,l) print(g) #{'x': 100, 'y': 2,'z':200,......} print(l) #{'m': 300}

 

The creation of four custom metaclass control classes StanfordTeacher

A class does not declare its own metaclass. By default, its metaclass is type. In addition to using the built-in metaclass type, we can also customize the metaclass by inheriting type, and then use the metaclass keyword parameter to specify the metaclass for a class.

class Mymeta(type): #只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类 pass # StanfordTeacher=Mymeta('StanfordTeacher',(object),{...}) class StanfordTeacher(object,metaclass=Mymeta): school='Stanford' def __init__(self,name,age): self.name=name self.age=age def say(self): print('%s says welcome to the Stanford to learn Python' %self.name)

 

The custom metaclass can control the generation process of the class. The generation process of the class is actually the calling process of the metaclass, that is, StanfordTeacher = Mymeta ('StanfordTeacher', (object), {...}), calling Mymeta will first generate an empty The object StanfordTeacher, and then pass the parameters within the brackets of calling Mymeta to the __init__ method under Mymeta to complete the initialization, so we can

class Mymeta(type): #只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类 def __init__(self,class_name,class_bases,class_dic): # print(self) #<class '__main__.StanfordTeacher'> # print(class_bases) #(<class 'object'>,) # print(class_dic) #{'__module__': '__main__', '__qualname__': 'StanfordTeacher', 'school': 'Stanford', '__init__': <function StanfordTeacher.__init__ at 0x102b95ae8>, 'say': <function StanfordTeacher.say at 0x10621c6a8>} super(Mymeta, self).__init__(class_name, class_bases, class_dic) # 重用父类的功能 if class_name.islower(): raise TypeError('类名%s请修改为驼峰体' %class_name) if '__doc__' not in class_dic or len(class_dic['__doc__'].strip(' \n')) == 0: raise TypeError('类中必须有文档注释,并且文档注释不能为空') # StanfordTeacher=Mymeta('StanfordTeacher',(object),{...}) class StanfordTeacher(object,metaclass=Mymeta): """  类StanfordTeacher的文档注释  """ school='Stanford' def __init__(self,name,age): self.name=name self.age=age def say(self): print('%s says welcome to the Stanford to learn Python' %self.name)

 

Five custom metaclass control class StanfordTeacher call

Reserve knowledge: __call__

class Foo:
    def __call__(self, *args, **kwargs): print(self) print(args) print(kwargs) obj=Foo() #1、要想让obj这个对象变成一个可调用的对象,需要在该对象的类中定义一个方法__call__方法,该方法会在调用对象时自动触发 #2、调用obj的返回值就是__call__方法的返回值 res=obj(1,2,3,x=1,y=2)

 

It is known from the above example that calling an object triggers the execution of the __call__ method in the class of the object. If StanfordTeacher is also regarded as an object, then there must be a __call__ method in the class of StanfordTeacher.

class Mymeta(type): #只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类 def __call__(self, *args, **kwargs): print(self) #<class '__main__.StanfordTeacher'> print(args) #('lili', 18) print(kwargs) #{} return 123 class StanfordTeacher(object,metaclass=Mymeta): school='Stanford' def __init__(self,name,age): self.name=name self.age=age def say(self): print('%s says welcome to the Stanford to learn Python' %self.name) # 调用StanfordTeacher就是在调用StanfordTeacher类中的__call__方法 # 然后将StanfordTeacher传给self,溢出的位置参数传给*,溢出的关键字参数传给** # 调用StanfordTeacher的返回值就是调用__call__的返回值 t1=StanfordTeacher('lili',18) print(t1) #123

 

By default, calling t1 = StanfordTeacher ('lili', 18) will do three things

1. Generate an empty object obj

2. Call the __init__ method to initialize the object obj

3. Return the initialized obj

Correspondingly, the __call__ method in the StanfordTeacher class should also do these three things

class Mymeta(type): #只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类 def __call__(self, *args, **kwargs): #self=<class '__main__.StanfordTeacher'> #1、调用__new__产生一个空对象obj obj=self.__new__(self) # 此处的self是类OldoyTeacher,必须传参,代表创建一个StanfordTeacher的对象obj #2、调用__init__初始化空对象obj self.__init__(obj,*args,**kwargs) #3、返回初始化好的对象obj return obj class StanfordTeacher(object,metaclass=Mymeta): school='Stanford' def __init__(self,name,age): self.name=name self.age=age def say(self): print('%s says welcome to the Stanford to learn Python' %self.name) t1=StanfordTeacher('lili',18) print(t1.__dict__) #{'name': 'lili', 'age': 18}

 

The __call__ in the above example is equivalent to a template, we can rewrite the logic of __call__ on this basis to control the process of calling StanfordTeacher, for example, all the properties of StanfordTeacher's object become private

class Mymeta(type): #只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类 def __call__(self, *args, **kwargs): #self=<class '__main__.StanfordTeacher'> #1、调用__new__产生一个空对象obj obj=self.__new__(self) # 此处的self是类StanfordTeacher,必须传参,代表创建一个StanfordTeacher的对象obj #2、调用__init__初始化空对象obj self.__init__(obj,*args,**kwargs) # 在初始化之后,obj.__dict__里就有值了 obj.__dict__={'_%s__%s' %(self.__name__,k):v for k,v in obj.__dict__.items()} #3、返回初始化好的对象obj return obj class StanfordTeacher(object,metaclass=Mymeta): school='Stanford' def __init__(self,name,age): self.name=name self.age=age def say(self): print('%s says welcome to the Stanford to learn Python' %self.name) t1=StanfordTeacher('lili',18) print(t1.__dict__) #{'_StanfordTeacher__name': 'lili', '_StanfordTeacher__age': 18}

The above example involves the problem of finding attributes, such as self .__ new__, please see the next section

 

Five look at attribute search

Combining the implementation principle of python inheritance + metaclass to look at what the attribute search should look like? ? ?

 

After learning metaclasses, in fact, the classes we customize with class are all objects (including the object class itself is also an instance of metaclass type, which can be viewed with type (object)). Considering the class as an object, the following inheritance should be said to be: the object StanfordTeacher inherits the object Foo, the object Foo inherits the object Bar, and the object Bar inherits the object object

class Mymeta(type): #只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类 n=444 def __call__(self, *args, **kwargs): #self=<class '__main__.StanfordTeacher'> obj=self.__new__(self) self.__init__(obj,*args,**kwargs) return obj class Bar(object): n=333 class Foo(Bar): n=222 class StanfordTeacher(Foo,metaclass=Mymeta): n=111 school='Stanford' def __init__(self,name,age): self.name=name self.age=age def say(self): print('%s says welcome to the Stanford to learn Python' %self.name) print(StanfordTeacher.n) #自下而上依次注释各个类中的n=xxx,然后重新运行程序,发现n的查找顺序为StanfordTeacher->Foo->Bar->object->Mymeta->type

 

Therefore, the attribute search should be divided into two layers, one layer is the object layer (MRO based on c3 algorithm) search, and the other layer is the class layer (metaclass layer) search

#查找顺序:
#1、先对象层:StanfordTeacher->Foo->Bar->object
#2、然后元类层:Mymeta->type

 

Based on the above summary, let's analyze the search for self .__ new__ in __call__ in the metaclass Mymeta

class Mymeta(type): n=444 def __call__(self, *args, **kwargs): #self=<class '__main__.StanfordTeacher'> obj=self.__new__(self) print(self.__new__ is object.__new__) #True class Bar(object): n=333 # def __new__(cls, *args, **kwargs): # print('Bar.__new__') class Foo(Bar): n=222 # def __new__(cls, *args, **kwargs): # print('Foo.__new__') class StanfordTeacher(Foo,metaclass=Mymeta): n=111 school='Stanford' def __init__(self,name,age): self.name=name self.age=age def say(self): print('%s says welcome to the Stanford to learn Python' %self.name) # def __new__(cls, *args, **kwargs): # print('StanfordTeacher.__new__') StanfordTeacher('lili',18) #触发StanfordTeacher的类中的__call__方法的执行,进而执行self.__new__开始查找

To sum up, self .__ new__ in __call__ under Mymeta will find __new__ in object if there is no __new__ in StanfordTeacher, Foo, Bar, and there is a __new__ by default in object, So even if the previous class has not implemented __new__, it will definitely find one in the object, it will not, and there is no need to find the metaclass Mymeta-> type to find __new__

 

 

We can also use object .__ new __ (self) to create objects in the __call__ of the metaclass

However, we still recommend using self .__ new __ (self) in __call__ to create empty objects, because this method will retrieve three classes StanfordTeacher-> Foo-> Bar, and object .__ new__ directly crosses them Three

One final note

class Mymeta(type): #只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类 n=444 def __new__(cls, *args, **kwargs): obj=type.__new__(cls,*args,**kwargs) # 必须按照这种传值方式 print(obj.__dict__) # return obj # 只有在返回值是type的对象时,才会触发下面的__init__ return 123 def __init__(self,class_name,class_bases,class_dic): print('run。。。') class StanfordTeacher(object,metaclass=Mymeta): #StanfordTeacher=Mymeta('StanfordTeacher',(object),{...}) n=111 school='Stanford' def __init__(self,name,age): self.name=name self.age=age def say(self): print('%s says welcome to the Stanford to learn Python' %self.name) print(type(Mymeta)) #<class 'type'> # 产生类StanfordTeacher的过程就是在调用Mymeta,而Mymeta也是type类的一个对象,那么Mymeta之所以可以调用,一定是在元类type中有一个__call__方法 # 该方法中同样需要做至少三件事: # class type: # def __call__(self, *args, **kwargs): #self=<class '__main__.Mymeta'> # obj=self.__new__(self,*args,**kwargs) # 产生Mymeta的一个对象 # self.__init__(obj,*args,**kwargs) # return obj

Six homework

Guess you like

Origin www.cnblogs.com/heirenxilou/p/12706413.html