Skip to main content

元类介绍

## 元类----------------> 用来实例化产生类的那个类
## 关系 : 元类---------------实例化 --------------->类----------------------> 对象

class People:
	def __init__(self, name, age):
		self.name = name
		self.age = age

	def say(self):
		print('<%s:%s>' % (self.name, self.age))

## 查看内置元类
print(type(People))  # <class 'type'>
print(type(int))     # <class 'type'>

## class关键字定义的类和内置的类都是由type产生的

1. class关键字创建类的步骤

# 类三大特征:类名 class_name || 类的基类  clas_bases = (Object) || 类体本身 --> 一对字符串(执行类体代码,拿到运行空间)

class People:
	def __init__(self, name, age):
		self.name = name
		self.age = age

	def say(self):
		print('<%s:%s>' % (self.name, self.age))
class_body = '''
	def __init__(self, name, age):
		self.name = name
		self.age = age

	def say(self):
		print('<%s:%s>' % (self.name, self.age))
'''
class_dic = {} # 定义类的命名空间

# 类名 
	class_name = "People"
# 类的基类  
	clas_bases = (object,)
# 执行类体代码,拿到运行空间
    exec(class_body,{},class_dic)  # 空字典指的是全局命名空间  class_dic是类的命名空间 
    ## 运行拿到exec以后可以拿到类体代码的运行空间,放在class_dic 里

    print(class_dic) 
    ## {'__init__': <function __init__ at 0x0000016A0BDC9900>, 'say': <function say at 0x0000016A0BE2E320>}

# 调用元类
	People = type(class_name,class_basis,class_dic)
    print(People)  ## <class '__main__.People'>

2. 定制元类,控制类的产生

## 定制元类
class Mymeta(type): ## 只有继承了type类的类才可以称之为元
    ## 运行__init__方法的时候,空对象和这些class_name,class_basis,class_dic一共四个参数一起传进来了
    ## 所以需要四个参数接受
    
    ## 重写了造对象的方法,不写__new__方法的话自动创建空对象
    ## 参数为: 类本身,调用类时所传入的参数
    def __new__(xls,*args,**kwargs):
        ##第一种方法 ----------------> 调父类的__new__()方法造对象 
        return super().__new__(cls,*args,**kwargs)
    	## 第二种方法 -----------------> 调用元类的内置方法
        return type.__new__(cls,*args,**kwargs)

	## 可以控制类的产生
    def __init__(self,class_name,class_basis,class_dic):
        ## 类的首字母大写
        if not x.capitalize():
            raise NameError('类名的首字母必须大写啊!!!!')     
     


class People(object ,metaclass = Mymeta): 
    # class产生类的话会自动继承object
    # 底层的话需要明确之指定继承object类
	def __init__(self, name, age):
		self.name = name
		self.age = age

	def say(self):
		print('<%s:%s>' % (self.name, self.age))
        
class_body = '''
	def __init__(self, name, age):
		self.name = name
		self.age = age

	def say(self):
		print('<%s:%s>' % (self.name, self.age))
'''
class_dic = {} # 定义类的命名空间

# 类名 
	class_name = "People"
# 类的基类  
	clas_bases = (object,)
# 执行类体代码,拿到运行空间
    exec(class_body,{},class_dic) 
# 调用元类
	People = Mymeta(class_name,class_basis,class_dic)  ## 调用 type.__call__(方法)
    ## 将参数先传给 __new__方法,造空对象
    ## 然后参数传递给 __init__方法初始化类
    
    ## 调用Mymeta发生的事儿,调用Mymeta 就是type.__call__()
    	# 先造一个空对象 ==> People 调用__new__方法
        #  调用Mymeta这个类的__inti__方法,完成初始化对象的操作(这个过程中可以控制类的产生)
        # 返回初始化好的对象
      
## 总结
	# 控制造空对象过程   重写 __new__()方法
    # 控制类的产生  重写 __init__()方法

3. new(方法)

## 具体看 2 控制造空对象的过程
##  __new__() 放下造对象时,早于 __init__() 方法运行  

4. call(方法)

## 1 中
# 调用元类时
    People = Mymeta(class_name,class_basis,class_dic)  ## 本质就是调用 type的__call__()方法
   

class Foo:
    def __init__(self,x,y):
        self.x = x
        self.y = y
    def __call__(self,name,age):
        print(name,age)
        print('我运行了obj下面的__call__方法')
        
obj = Foo(111,222)
obj("yanchen",'18') 
# 'yanchen' 18
# '我运行了obj下面的__call__方法'

## 对象的类里定义__call__方法的话,实例对象可以调用

### -------------------------------------------------------------------
## 如果想要控制类的调用, 那就重写__call__()方法
## 定制元类
class Mymeta(type):  
    def __call__(self,*args,**kwargs):
        ## Mymeta.__call__函数内会调用People.__new__()方法
        people_obj = self__new__(self)
        
        ## 可以对类进行定制化
        obj.__dict__['xxxx'] ='所有的obj产生的类我新加了一个属性'
        
        ## Mymeta.__call__函数内调用People.__inti__()方法
        self.__init__(people_obj,*args,**kwargs)
        
    ## 重写了造对象的方法,不写__new__方法的话自动创建空对象
    ## 参数为: 类本身,调用类时所传入的参数
    def __new__(xls,*args,**kwargs):
        ##第一种方法 ----------------> 调父类的__new__()方法造对象 
        return super().__new__(cls,*args,**kwargs)
        ## 第二种方法 -----------------> 调用元类的内置方法
        return type.__new__(cls,*args,**kwargs)

    ## 可以控制类的产生
    def __init__(self,class_name,class_basis,class_dic):
        ## 类的首字母大写
        if not x.capitalize():
            raise NameError('类名的首字母必须大写啊!!!!')     
     

        
class People(object ,metaclass = Mymeta): 
    # class产生类的话会自动继承object
    # 底层的话需要明确之指定继承object类
    def __init__(self, name, age):
        self.name = name
        self.age = age
        
    def __new__(cls,*args,**kwargs):
        # 造对象
        return object.__new__(cls,*args,**kwargs)
        

    def say(self):
        print('<%s:%s>' % (self.name, self.age))
 ## ------------------------------------------------------------        
 class_body = '''
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def say(self):
        print('<%s:%s>' % (self.name, self.age))
'''
class_dic = {} # 定义类的命名空间
class_name = "People" 
clas_bases = (object,)
exec(class_body,{},class_dic) 
 People = Mymeta(class_name,class_basis,class_dic)  ## 调用 type.__call__(方法)     
    
  ## 调用Mymeta发生的事儿,调用Mymeta 就是type.__call__()
        # 先造一个空对象 ==> People 调用__new__方法
        #  调用Mymeta这个类的__inti__方法,完成初始化对象的操作(这个过程中可以控制类的产生)
        # 返回初始化好的对象
        
             
  obj = People('yanchen',18)
    # 实例化People发生的三件事
        # 调用Mymeta.__call__(self,*args,**kwargs)方法
        #  Mymeta.__call__函数内会调用People.__new__()方法
        #  Mymeta.__call__函数内调用People.__inti__()方法

5. 属性查找的原则

image-20230704003043593.png

## 属性查找的原则:对象 ----> 类 ----------->父类
## 切记:父类不是元类,不会去从元类里找