Python的元类

一、元类的介绍

1、引入:一切都源自一句话:一切皆对象

class People:
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def say(self):
        print('%s:%s'%(self.name,self.age))

# 如何得到对象?  答:变量名obj=调用类()
obj = People('lsj',18)
  类也是对象,变量 = 调用类(),其实是在使用python关键字class定义类的时候,底层代码帮我们实现了这个功能
  People = 调用类(....)
2、什么是元类:
  把类本身当作对象,这种类叫元类:就是用来实例化产生类的类

  关系:元类--->实例化--->类(People)--->实例化-->对象(obj)
# 如何查看内置的元类:
# 1、type是内置的元类
# 2、我们用class关键字定义的所有的类以及内置的类都是由元类type()实例化产生的,所以我们自定义的所有的类祖师爷都是type
print(type(obj))    # <class '__main__.People'>
print(type(People)) # <class 'type'>
# 查看一下自定义类型和内置类型都是type类
print(type(int))  # <class 'type'>
print(type(People)) # <class 'type'>

 二、class机制

# 分析:type调用了元类People= type(...)

# class People():
#     def __init__(self,name,age):
#         self.name = name
#         self.age = age
#     def say(self):
#         print('%s:%s'%(self.name,self.age))
# 查看名称空间
print(People.__dict__)
"""
{'__module__': '__main__', '__init__': <function People.__init__ at 0x00000241001214C0>, 'say': <function People.say at 0x0000024100121550>, '__dict__': <attribute '__dict__' of 'People' objects>, '__weakref__': <attribute '__weakref__' of 'People' objects>, '__doc__': None}
"""
# 刨析class机制创造类的工作原理有四步。
# 类有三大特征:
# 1、类名
class_name = 'People'
# 2、类的基类(class People():),没有继承类就放一个object类,以元组形式保存
class_bases = (object,)
# 3、类体,类体本身就是字符串,但是执行类体代码拿到的最终的结果是类的名称空间
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 = {}
exec(class_body,{},class_dic) # exec()的功能是:将class_body代码运行,将产生的名字放到class_dic里,{}表示全局名称空间
print(class_dic)  # {'__init__': <function __init__ at 0x0000016A0FE01430>, 'say': <function say at 0x0000016A0FE014C0>}
# 4、调用元类:class,使用type(类名,类的基类,类的名称空间)
print(type(class_name,class_bases,class_dic))  # <class '__main__.People'>
x = type(class_name,class_bases,class_dic)
print(x)  # <class '__main__.People'>
People = type(class_name,class_bases,class_dic)
print(People) # <class '__main__.People'>
# 所以上面的People只是个变量名,但是这个变量名的值才是类

 

 三、如何自定义元类来控制类的产生

1、定制元类控制类的产生

# class People(A,B,metaclass=type):  # 相当于继承了A,B两个类,metaclass=type调用了内置的元类
# class People(metaclass=type):  # 相当于继承了object类
# 自定义一个名叫Mymeta的元类
class Mymeta(type): # 注意:这里必须继承type,只有继承了type的类才叫元类
    # 空对象,class_name,class_bases,class_dic=("People",(object,),{...})
    def __init__(self,):print('run...') # run...

# People = Mymeta(class_name,class_bases,class_dic)
# 调用Mymeta发生三件事
# 1、先造一个空对象==>People
# 2、调用Mymeta这个类内的__init__方法,完成初始化对象的操作。
# 3、返回初始化好的对象
class People(metaclass=Mymeta):  # metaclass=Mymeta调用了我自定义的元类
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def say(self):
        print('%s:%s'%(self.name,self.age))
# People = type(class_name,class_bases,class_dic)
# People = Mymeta(class_name,class_bases,class_dic)

# class People(metaclass=Mymeta):报错位置
# TypeError: __init__() takes 1 positional argument but 4 were given
# 针对报错:修改自定义的元类
class Mymeta(type): # 注意:这里必须继承type,只有继承了type的类才叫元类
    # 空对象,class_name,class_bases,class_dic=("People",(object,),{...})
    # def __init__(self,):
    def __init__(self,x,y,z):
        print('run...') # run...
        print(self) # <class '__main__.People'>
        print(x)    # 类名:People
        print(y)    # 类的基类:()
        print(z)    # 类的名称空间:{'__module__': '__main__', '__qualname__': 'People', '__init__': <function People.__init__ at 0x0000019F99581550>, 'say': <function People.say at 0x0000019F995815E0>}
# run...
# Mymeta对象是People
print(y)  # 类的基类:() 我们看到基类是空的,是因为class People(metaclass=Mymeta):中没有继承任何的类
如果class People(object,metaclass=Mymeta):
print(y)  # 运行结果就是 (<class 'object'>,)

 2、自定义类时类名必须大写的提示:

# 自定义元类
class Mymeta(type): # 注意:这里必须继承type,只有继承了type的类才叫元类
    # 空对象,class_name,class_bases,class_dic=("People",(object,),{...})
    # def __init__(self,):
    def __init__(self,x,y,z):
        if not x.istitle():
            raise NameError('类名的首字母必须大写')

# 自定义的类
# class people(object,metaclass=Mymeta):  # metaclass=Mymeta调用了我自定义的元类
class People(object,metaclass=Mymeta):  # metaclass=Mymeta调用了我自定义的元类
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def say(self):
        print('%s:%s'%(self.name,self.age))

# NameError: 类名的首字母必须大写
# 所以把自定义的类名改成大写就可以了

 3、__new__方法:调用一个类时发生的三件事,在第一步中先造空对象,我们要用到__new__方法

class Mymeta(type):
    # 空对象,class_name,class_bases,class_dic=("People",(object,),{...})
    def __init__(self,x,y,z):
        print('run222')
        print(y)  # 类的基类:()
        print(self.__bases__)  # (<class 'object'>,)
        if not x.istitle():
            raise NameError('类名的首字母必须大写')
    # 我们重写new方法,来造对象。
    def __new__(cls, *args, **kwargs): # 当前所在的类,调用类时所传入的参数
        # 造Mymeta对象
        print('run111111....')
        print(cls,args,kwargs)
        # 自己这里没有我们跟谁要?1、跟父类要。2、指名道姓的要
        # return super().__new__(cls,*args,**kwargs) # 1、跟父类要
        return type.__new__(cls,*args,**kwargs) # 2、指名道姓的要

# 调用一个类(Mymeta)会发生的三件事。调用Mymeta就是type.__call__ 
# 1、先造一个空对象==>People,调用类内的__new__方法来造空对象
# 2、调用Mymeta这个类内的__init__方法,完成初始化对象的操作。
# 3、返回初始化好的对象

class People(metaclass=Mymeta):
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def say(self):
        print('%s:%s'%(self.name,self.age))

# 只要是调用类,那么会依次调用
# 1、第一步调用类内的__new__
# 2、第二步调用类内的__init__

 4、__call__方法

# 返回初始化好的对象,到底返回的是谁?
# call方法
class Foo:
    def __init__(self,x,y):
        self.x = x
        self.y = y
# Foo调用,Foo()    
obj = Foo(111,222)
# obj可以调用么?如下所示 obj()
# 当obj调用时会报错:TypeError: 'Foo' object is not callable 针对这个报错,我们要在Foo类中加入call方法
class Foo:
    def __init__(self,x,y):
        self.x = x
        self.y = y
    def __call__(self, *args, **kwargs):
     print('==》')
obj = Foo(111,222) print(obj) # 触发的是obj.__str__ 运行结果:<__main__.Foo object at 0x0000021C1CFB44F0>
obj() # 触发的是obj.__call__ 运行结果:==》
class Foo:
    def __init__(self,x,y):
        self.x = x
        self.y = y
    def __call__(self, *args, **kwargs):
        print('==》')
        return 123

obj = Foo(111,222)

res = obj()   # 获取返回值
print(res)    # 如果call方法下没有return得到结果是:None。有return得到的是return后面的结果123
总结应用:如果想让一个对象可以加()括号调用,需要在该对象的类中添加一个方法__call__
class Foo:
    def __init__(self,x,y):
        self.x = x
        self.y = y
    def __call__(self,*args,**kwargs):
        print('==》',args,kwargs)
        return 123

obj = Foo(111,222)
res = obj(1,2,3,4,a=5,b=6)   # obj传入参数,结果获取返回值
print(res) 
# ==》 (1, 2, 3, 4) {'a': 5, 'b': 6}
# 123

 5、总结

对象()-->类内的__call__
类()-->元类内的__call__
自定义元类() -->内置元类__call__
# 定制元类控制类的产生
# 自定义元类控制类的调用-->类的对象的产生
class Mymeta(type): # 注意:这里必须继承type,只有继承了type的类才叫元类
    def __init__(self,x,y,z):
        print('run2222222....')
    def __new__(cls, *args, **kwargs):
        print('run1111111....')
        return type.__new__(cls,*args,**kwargs)
# 类的产生
# People = Mymeta()  # 内置的元类type.__call__==》干了三件事:
# 1、type.__call__函数内会先调用Mymeta内的__new__
# 2、type.__call__函数内会先调用Mymeta内的__init__
# 3、type.__call__函数内会返回一个初始化好的对象

class People(object,metaclass=Mymeta):  # metaclass=Mymeta调用了我自定义的元类
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def say(self):
        print('%s:%s'%(self.name,self.age))# 类的调用
obj = People('lsj',18)

# 我们研究一下People的调用,是在调用谁?
# obj = People('lsj',18)  -->Mymeta.__call__-->干了三件事:
# 1、Mymeta.__call__函数内会先调用People内的__new__
# 2、Mymeta.__call__函数内会先调用People内的__init__
# 3、Mymeta.__call__函数内会返回一个初始化好的对象

print(obj)  
对Mymeta进行修改
class
Mymeta(type): # 注意:这里必须继承type,只有继承了type的类才叫元类 def __call__(self, *args, **kwargs): # 1、Mymeta.__call__函数内会先调用People内的__new__ people_obj = self.__new__(self) # 2、Mymeta.__call__函数内会先调用People内的__init__ self.__init__(people_obj,*args,**kwargs) # 3、Mymeta.__call__函数内会返回一个初始化好的对象 # print(self) # <class '__main__.People'> # print(args) # ('lsj', 18) # print(kwargs) # {} # return 11111 return people_obj # <__main__.People object at 0x0000023483307700>
class People(object,metaclass=Mymeta):  # metaclass=Mymeta调用了我自定义的元类
def __init__(self,name,age):
self.name = name
self.age = age
def say(self):
print('%s:%s'%(self.name,self.age))
def __new__(cls, *args, **kwargs):
# 产生真正的对象
return object.__new__(cls)
obj = People('lsj',18)
print(obj)  # <__main__.People object at 0x0000029B61717700>
print(obj.__dict__) # {'name': 'lsj', 'age': 18}

 

# 我要把dict里的属性变成隐藏的属性
class Mymeta(type): # 注意:这里必须继承type,只有继承了type的类才叫元类
    def __call__(self, *args, **kwargs):
        # 1、Mymeta.__call__函数内会先调用People内的__new__
        people_obj = self.__new__(self)
        # 2、Mymeta.__call__函数内会先调用People内的__init__
        self.__init__(people_obj,*args,**kwargs)
        # 3、Mymeta.__call__函数内会返回一个初始化好的对象
        print('people对象的属性:',people_obj.__dict__)
        people_obj.__dict__['xxxx'] = 11
        return people_obj  # <__main__.People object at 0x0000023483307700>
class People(object,metaclass=Mymeta):  # metaclass=Mymeta调用了我自定义的元类
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def say(self):
        print('%s:%s'%(self.name,self.age))
    def __new__(cls, *args, **kwargs):
        # 产生真正的对象
        return object.__new__(cls)

# 类的调用
obj = People('lsj',18)

print(obj)  # <__main__.People object at 0x0000029B61717700>
print(obj.__dict__) # {'name': 'lsj', 'age': 18, 'xxxx': 11}

 

 

6、元类的属性查找

属性查找的原则:对象->类->父类
切记:父类不是元类
在学习完元类后,其实我们用class自定义的类也全都是对象(包括object类本身也是元类type的 一个实例,可以用type(object)查看),
我们学习过继承的实现原理,如果把类当成对象去看,将下述继承应该说成是:对象StanfordTeacher继承对象Foo,对象Foo继承对象Bar,
对象Bar继承对象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

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

posted @ 2020-05-14 16:16  思江  阅读(235)  评论(0编辑  收藏  举报