• 元类


    元类

    1.什么是元类

    在python中一切皆对象,那我们用class关键字定义的类本身也是一个对象,负责产生
    该对象的类称之为元类,即元类可以简称为类的类.

    class Foo:  # Foo=元类
    	pass
    
    元类(type) --实例化 --> 我们所使用的的类(class 类名)--实例化--> 对象
    

    2.为什么用元类

    - 元类就是负责产生类的,所以我们学习元类或者定义元类的目的:就是为了控
    制类对的生产过程,还可以控制对象的产生过程
    

    3.内置函数exec(以后用的,先学着)

    cmd = """
    x = 1
    print('exec函数运行了')
    def func(self):
    	pass
    """
    class_dic = {}
    # 执行cmd中的代码,然后把产生的名字丢入class_dic字典中
    exec(cmd,{},class_dic)
    
    exec函数运行了
    print(class_dic)
    

    4.class创建类

    - 如果说类也是对象,那么用class关键字取常见类的过程也是一个实例化的过程
    ,该实例化的目的是为了得到一个类,调用的是元类
    - 用class关键字创建一个类,用的默认的元类type,因此以前说不要用type作为
    类别判断
    
    	class People:
    		country = 'China'
    		
    		def __init(self,name,age):
    			self.name = name
    			self.age = age
    			
    		def eat(self):
    			print(f"{self.name} is eating...")
    			
    	print(type(People)) # 打印--> <class 'type'>
    

    4.type实现

    - 创建类的三个要素:类名,基类,类的名称空间
    - People = type(类名,基类(父类),名称空间)
    
    	class_name = "People" # 类名
    	
    	class_bases = (object,) #基类,我们默认都是object
    	
    	# 累的名称空间
    	class_dic = {}
    	class_code = """
    	country = 'China'
    	def __init__(self,name,age):
    		self.name = name
    		self.age = age
    	def eat(self):
    		print(f"{self.name} is eating...")
    		
    	
    	"""
    	exec(class_code,{},class_dic)
    	
    	#---------------打印前面的属性
    	print(class_name) # 打印--> "People"
    	print(class_bases) # 打印 --> (<class 'object'>,)
    	print(class_dic) # 打印 --> {'country': 'China', '__init__': <functi
    	on __init__ at 0x10a0bc048>, 'eat': <function eat at 0x10a0bcd08>}
    	#-----------------------------
    	#----------------调用生成对象,调用方法
    	People = type(class_name,class_bases,class_dic)
    	print(People)  # <class '__main__.People'>
    	
    	obj1 = People(1,2)
    	obj1.eat()  # 打印-->1 is eating...
    

    5.自定义元类控制类的创建

    - 使用自定义元类
    ```python
    class Mymeta(type): # 只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类
    	def __init__(self,class_name,class_bases,class_dic):
    		print('self:',self) # 现在是Peole
    		print('class_name:',class_name)
    		pring('class_bases:',class_bases)
    		print('class_dic:',class_dic)
    		super().__init__(class_name,class_bases,class_dic) # 重用父类type的功能
    		
    ```
    - 分析用class自定义类的原理(而非元类的运行原理)
    	- 拿到一个字符串格式的类名class_name = "People"
    	- 拿到一个类额基类们class_bases = (object,)
    	- 执行类体(exec函数)代码,拿到一个类的名称空间class_dic = {...}
    	- 调用People = type(class_name,class_bases,class_dic)
    ```python
    class People(object,metaclass=Mymeta): # People=Mymeta(类名,基类们,类的名称空间)
    	country = 'China'
    	
    	def __init__(self,name,age):
    		self.name = name
    		self.age = age
    	
    	def eat(self):
    		print(f"{self.name} is eating")
            
    ```
    

    5.1 应用

    - 自定义元类控制类的产生过程,类的产生过程其实就是元类的调用过程
    - 我们可以控制类必须有文档,可以使用如下的方式实现
    ```python
    
        # 注释以及首字母大写判断
        class Mymeta(type) # 只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类
            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('类中必须有注释,并且不能为空')
                if not class_name.istitle():
                    raise TypeError('首字母必须大写')
                super().__init__(class_name,class_bases,class_dic) # 重用父类功能
    


    class People(object,metaclass=Mymeta):
    # !!! People = Mymeta('People',(object,),{名称空间}})
    # '''这是People类'''
    country = 'China'
    def init(self,name,age):
    self.name = name
    self.age = age

            def eat(self):
                print(f"{self.name} is eating...")
    
    
        # 抛出我们定义的异常,因为我们所定义的继承元类的类没有我们按照
        # Mymeta中规定的形式创建一个符合条件的类,所以会报错
        
    ```
    

    6.call(储备)

    - 要想让obj这个对象变成一个可调用的对象,需要在该对象的类中定义一个方法
    __call__方法,该方法在调用对象的时候自动触发
    
    ```python
        class Foo:
            def __call__(self,*args,**kwargs):
                print(args)
                print(kwargs)
                print('__call__实现了实例化对象可以加括号调用')
                
        obj = Foo()
        obj('cheer',age=18)
        
        #('cheer')
        #{'age':18}
        # __call__实现了实例化对象可以加括号调用
    

    7.new(储备)

    我们之前所说类实例化第一个调用的是__init__,但__init__其实并不是实例化
    类de时候第一个调用的方法,在我们使用类实例化的时候,他会生成一个框架,在
    哪之后才会将class_name,class_bases,class_dic,添加进去,返回一个完整的
    类,其实这个最先被调用的方法是__new__方法.
    __new__方法接受的参数虽然也和__init__一样,但__init__是在类实例创建之
    后调用的,而__new__方法正是创建这个类的方法.
    ```python
        class A:
            pass
          
        class B(A):
            def __new__(cls):
                print("__new__方法")
                return object.__new__(cls) # 这里应该使用object,不然会一直循环
                                           # 使用object的__new__方法执行我们的类
             
            def __init__(self):
                print("__init__方法")
                
        b = B()
    ```
    

    8.自定义元类控制类的实例化

        class Mymeta(type):
            def __call__(self,*args,**kwargs):
                print(self)  # self是People
                print(args) # args = ('cheer')
                print(kwargs) # kwargs = {'age':18)}
                # return 123
                # 1.先造出一个People的空对象,申请内存空间,涉及到底层的c,指针
                # __new__方法接受的参数虽然也和__init__一样,但是__init__是在类实例创建之后调用的,er__new__方法正是创建这个类实例的方法.
                obj = self.__new__(self) # 虽然和下面同样是People,但是People没有,找到的__new__是父类的
                # 2.为该对象初始化独有的属性
                self.__init__(obj,*args,**kwargs)
                # 3.返回一个初始化的对象
                return obj
    
    - People = Mymeta(),People()则会触发__call__
    
        class People(object,metaclass=Mymeta):
            country = "China"
            
            def __init__(self,name,age):
                self.name = name
                self.age = age
            
            def eat(self):
                print(f"{self.name} is eating...")
                
        # 在调用Mymeta的__call__的时候,首先会找到(如下函数)的,自己的没有才会去找父类的
        # def __new__(cls,*args,**kwargs):
            # print(cls) # cls是People
            # cls.__new__(cls) # 错误,无限死循环,自己找自己的,会出现无限递归
        #   obj = supper().__new__(cls) # 使用父类的__new__
        #   return obj
    
    - 类的调用,即类实例化就是元类的调用过程,可以通过元类Mymeta的__call__方法控制
    - 分析:调用People的目的:
        - 先造出一个People的空对象(也就是一个模型)
        - 为该空对象初始化独有的属性
        - 返回一个初始化好的对象
    

    10.自定义元类后类的继承顺序

    查找顺序:

    -先对象层:对象->类->父类->object
    
    -然后元类层:元类->type
    



  • 相关阅读:
    学习SpringMVC——从HelloWorld开始
    线性队列
    线性表之链表
    线性表之顺序表
    nextSibling 属性与 nextElementSibling 属性的异同
    JavaScript数组增删方法总结
    class关键字
    JS三座大山_单线程&EventLoop
    JS三座大山_闭包
    JS三座大山_原型与原型链
  • 原文地址:https://www.cnblogs.com/xiongchao0823/p/11455290.html
Copyright © 2020-2023  润新知