Catalog
Meta class
Warning! Warning! The nuclear energy coming ahead!!! The little buddy who wants to get on the bus hurries on, time is running out... The conductor took care to weld the doors to me! Please don't jump freely on the way, jumping is not standard, family members two lines of tears... Passengers, we started off. Please pay attention to the safety of life during the journey. If you don't write your suicide note in time, the train will not be responsible for it.
What is metaclass
2. What is metaclass
- In python, everything is an object, so the class defined by the class keyword itself is an object. The class responsible for generating the object is called metaclass, which can be referred to as class class.
class Foo: # Foo = metaclass () pass
Why use metaclasses
Metaclasses are responsible for generating classes, so the purpose of learning metaclasses or custom classes is to control the generation process of classes and the generation process of objects.
Built-in function exec
cmd=''' x=1 print('exec The function is here') def func(self): pass ''' class_dic={} exec(cmd,{},class_dic) print(class_dic)
The purpose of this built-in method is to put the code in the cmd string into the dictionary in the form of key-to-value
class Creates Classes
- If a class is also an object, the process of creating a class with class keywords is also an instantiation process. The purpose of the instantiation is to get a class and call a metaclass.
- Create a class with the class keyword and use the default metaclass type, so it was previously said not to use type as a category judgment
class People: # People=type(...) country = 'China' def __init__(self, name, age): self.name = name self.age = age def eat(self): print('%s is eating' % self.name) print(type(People)) <class 'type'>
type implementation
cmd=''' x=1 print('exec The function is here') def func(self): pass ''' class_dic={} exec(cmd,{},class_dic) People=type('People',(object,),class_dic) print(People__name__)
Custom metaclass
class yuan(type): def __init__(self,name,bases,dic): if name.startswith('sb'): raise Exception('Can't sb Start') print(name) class sbPerson(object,metaclass=yuan): def __init__(self,name,age): self.name=name self.age=age def print_age(self): print(self.age) p=sbPerson('nick',19)
- Use custom metaclasses
class Mymeta(type): # Only by inheriting the type class can it be called a metaclass, otherwise it is a common custom class. def __init__(self, class_name, class_bases, class_dic): print('self:', self) # Now it's People print('class_name:', class_name) print('class_bases:', class_bases) print('class_dic:', class_dic) super(Mymeta, self).__init__(class_name, class_bases, class_dic) # Reuse the function of parent type
- Analyse the operation principle of custom classes with classes (rather than metaclasses):
- Get a class name in string format class_name='People'
- Get a class's base classes class_bases=(obejct,)
- Execute class body code to get a class namespace class_dic={...}
- Call People=type(class_name,class_bases,class_dic)
class People(object, metaclass=Mymeta): # People = Mymeta (class names, base classes, class namespaces) country = 'China' def __init__(self, name, age): self.name = name self.age = age def eat(self): print('%s is eating' % self.name) self: <class '__main__.People'> class_name: People class_bases: (<class 'object'>,) class_dic: {'__module__': '__main__', '__qualname__': 'People', 'country': 'China', '__init__': <function People.__init__ at 0x10a0bcbf8>, 'eat': <function People.eat at 0x10a0bc2f0>}
- A custom metaclass controls the generation process of a class. The generation process of a class is actually the calling process of a metaclass.
- We can control that classes must have documentation, which can be implemented in the following way
class Mymeta(type): # Only by inheriting the type class can it be called a metaclass, otherwise it is a common custom class. def __init__(self, class_name, class_bases, class_dic): if class_dic.get('__doc__') is None or len( class_dic.get('__doc__').strip()) == 0: raise TypeError('Classes must have document comments, and document comments cannot be empty') if not class_name.istitle(): raise TypeError('Class names must be capitalized') super(Mymeta, self).__init__(class_name, class_bases, class_dic) # Reuse the functions of parent classes try: class People(object, metaclass=Mymeta ): #People = Mymeta('People',(object,),{....}) # """"This is the People class""" country = 'China' def __init__(self, name, age): self.name = name self.age = age def eat(self): print('%s is eating' % self.name) except Exception as e: print(e) //Classes must have document comments, and document comments cannot be empty
_ _ call _ _
- To make obj a callable object, we need to define a method, _call_ method in the class of the object, which will trigger automatically when the object is called.
class Foo: def __call__(self, *args, **kwargs): print(args) print(kwargs) print('__call__Implemented, instantiated objects can be called in parentheses') obj = Foo() obj('nick', age=18) ('nick',) {'age': 18} __call__Implemented, instantiated objects can be called in parentheses
_ _ new _ _
We said before that class instantiation first calls _init_, but _init_ is not actually the first method to be called when a class is instantiated. When using expressions such as Persion(name, age) to instantiate a class, the first method to be called is actually the new method.
_ The new_ method accepts the same parameters as _init_, but _init_ is called after the class instance is created, and the _new_ method is the way to create the class instance.
Note: The new() function can only be used for new classes inherited from object s.
class A: pass class B(A): def __new__(cls): print("__new__Method is executed") return cls.__new__(cls) def __init__(self): print("__init__Method is executed") b = B()
Instance of custom metaclass control
class Mymeta(type): def __call__(self, *args, **kwargs): print(self) # self is People print(args) # args = ('nick',) print(kwargs) # kwargs = {'age':18} # return 123 # 1. Create an empty People object and apply for memory space. # _ The new_ method accepts the same parameters as _init_, but _init_ is called after the class instance is created, and the _new_ method is the way to create the class instance. obj = self.__new__(self) # Although it's the same as People below, People doesn't. The _new_ found is parent. # 2. Initialize unique properties for the null object self.__init__(obj, *args, **kwargs) # 3. Return an initialized object return obj
- People = Mymeta(), People() triggers _call__
class People(object, metaclass=Mymeta): country = 'China' def __init__(self, name, age): self.name = name self.age = age def eat(self): print('%s is eating' % self.name) # When calling Mymeta's _call_, you will first find yourself (the following function). If you don't have one, you will find the parent class. # def __new__(cls, *args, **kwargs): # # print(cls) # cls is People # # cls.__new__(cls) # Error, infinite dead cycle, find oneself, infinite recursion # obj = super(People, cls).__new__(cls) # If you use a parent class, you go to the parent class to find _new.__ # return obj
- Class invocation, that is, class instantiation, is the calling process of metaclass, which can be controlled by the _call_ method of metaclass Mymeta.
- Analysis: The purpose of calling Pepole
- First create an empty People object
- Initialize unique properties for this null object
- Returns an initialized object
obj = People('nick', age=18) <class '__main__.People'> ('nick',) {'age': 18} print(obj.__dict__) {'name': 'nick', 'age': 18}
Attribute lookup order
Combined with the implementation principle of python inheritance + metaclass, what should the search of attributes look like???
After learning metaclasses, all the classes we customize with class are objects (including object class itself is an instance of metaclass type, which can be viewed with type). We have learned the principle of inheritance. If we look at classes as objects, we should say the following inheritance is OldboyTeacher inheritance of objects. Object Foo, Object Foo inherits object Bar, Object Bar inherits object Bar
class Mymeta(type): # Only by inheriting the type class can it be called a metaclass, otherwise it is a common custom class. n = 444 def __call__(self, *args, **kwargs): #self=<class '__main__.OldboyTeacher'> obj = self.__new__(self) self.__init__(obj, *args, **kwargs) return obj class Bar(object): n = 333 class Foo(Bar): n = 222 class OldboyTeacher(Foo, metaclass=Mymeta): n = 111 school = 'oldboy' def __init__(self, name, age): self.name = name self.age = age def say(self): print('%s says welcome to the oldboy to learn Python' % self.name) print( OldboyTeacher.n ) # Annotate n=xxx in each class from bottom to top, and then run the program again. It is found that the search order of n is Oldboy Teacher - > Foo - > Bar - > Object - > Mymeta - > type. 111 print(OldboyTeacher.n) 111
- Search order:
- First Object Layer: Oldoy Teacher - > Foo - > Bar - > Object
- Then the metaclass layer: Mymeta - > type
Based on the above summary, we will analyze the search of self. new in _call_ of the lower metaclass Mymeta.
class Mymeta(type): n = 444 def __call__(self, *args, **kwargs): #self=<class '__main__.OldboyTeacher'> 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 OldboyTeacher(Foo, metaclass=Mymeta): n = 111 school = 'oldboy' def __init__(self, name, age): self.name = name self.age = age def say(self): print('%s says welcome to the oldboy to learn Python' % self.name) # def __new__(cls, *args, **kwargs): # print('OldboyTeacher.__new__') OldboyTeacher('nick', 18) # Triggers the execution of the _call_ method in OldboyTeacher's class, and then executes self. _new_ to start the lookup
To sum up, self. new in call under Mymeta will find new in object if it is not found in Oldboy Teacher, Foo and Bar, and there is new by default under object, so even if the previous classes have not been implemented new, they will certainly find one in object, not at all, not at all. Necessary to look for _new in the metaclass Mymeta - > type__