一、元类的介绍
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__
__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