
>>> class Test():
... pass
...
>>> type(Test) # 查看自定义类的类型
<class 'type'>
>>> type(int) # 查看python内置类型的类型
<class 'type'>
上述代码结果都为<class ‘type’>,type就是内置的元类,class关键字定义的所有的类以及内置的类都是由元类type实例化产生。
>>> object.__class__
<class 'type'>
>>> type.__class__
<class 'type'>
>>> object.__bases__
()
>>> type.__bases__
(<class 'object'>,)
>>> type(object)
<class 'type'>
>>> type(type)
<class 'type'>
在python3中类和类型是同一种东西,因此 对象.__class__ 和type(对象)得到的结果是一致的,object的基类为空,但是type的基类为object,但是object的类型又是type。
具体也是挺复杂的,简单来说,object和type是python中的两个源对象,如果有兴趣可以去python官方文档中找到答案。
参数一,包含一系列符合python语法代码的字符串;
参数二,字典形式的全局名称空间中的名字及所对应的值;
参数三,字典形式的局部名称空间中的名字及所对应的值;
# python字符串
strs = '''
global name, age # 全局名称
name = 'python'
age = 18
addr = 'xx' # 局部名称
'''
# 定义全局作用域中的名字和值
globals = {
'a': 1,
'b': 2
}
# 定义局部作用域中的名字和值
locals = {
'x': 3,
'y':4
}
exec(strs, globals, locals)
>>> globals
{'a': 1, 'b': 2, ..., 'name': 'python', 'age': 18}
>>> locals
{'x': 3, 'y': 4, 'addr': 'xx'}
了解了exec的作用之后,就可以分析class关键字如何借助type元类产生类的步骤:
# 1 定义类名
class_name = 'Test'
# 2 定义类的基类(父类)
class_bases = (object,)
# 3 执行类体代码拿到类的名称空间
class_dic = {}
# 4 定义类体代码(本质是字符串)
class_body = """
def __init__(self,name,age):
self.name=name
self.age=age
def test(self):
print('%s:%s' %(self.name,self.name))
"""
# 5 将字符串转为python能识别的语法:将class_body运行时产生的名字存入class_dic中
exec(class_body,{},class_dic)
# 查看类的名称空间
print(class_dic)
# 6 调用元类产生类
Test = type(class_name, class_bases, class_dic)
# 7 调用类产生对象
t = Test('python', '12')
我们可以通过控制调用元类的步骤控制元类的产生,元类的调用与自定义的普通类的调用步骤相同,所以,我们先来看一下一个类被调用的过程到底发生了什么事情吧!
# 定义一个类
class Test():
def __init__(self):
self.name = 'python'
def test(self):
print(self.name)
# 类实例化产生对象
t = Test()
基于之前的知识,我们知道类实例化产生对象时,会自动调用类中的__init__方法,为空对象增加独有属性,问题来了,这个空对象是从哪来的呢?所以说,在调用__init__方法之前肯定还调用了其他方法。
在python3中没有经典类和新式类之分,python3中自定义的普通类都默认继承了object类,所以在调用类产生对象时,在__init__方法运行之前的方法生成了一个空对象并返回,将这个空对象作为参数传给__init__方法,这个方法就是__new__方法。
在类加括号实例化对象时,首先会执行__new__方法,该方法早于__init__方法运行,返回一个空对象,如果该方法没有返回值,就不会调用__init__方法。
知道了类调用的步骤,就可以自定义元类来控制类的产生了。
# 自定义元类
class MyMeta(type): # 自定义元类必须继承type,否则就是普通的类
'''
早于__init__方法执行,必须返回空对象,由于该方法是调用类后第一个运行的方法,此时并没有对象产生, 因此该方法的第一个参数必须是类本身(MyMeta),*args, **kwargs用来接收调用元类产生对象所需的参数(类名 类的基类 名称空间)
'''
def __new__(cls,*args, **kwargs):
return type.__new__(cls, *args, **kwargs) # 直接调用父类type中的__new__方法
'''
通过__init__控制类的产生,调用自定义元类与调用内置元类type的方式相同,需要传入类名、类的父类们、类体代码的名称空间,__init__方法中第一个参数来自于__new__方法产生的空对象。
'''
def __init(self, class_name, class_bases, class_dict):
'''
在该方法内可以控制类的产生
'''
if not class_name.istitle(): # 实现类名首字母必须大写,否则抛出异常
raise NameError('类名的首字母必须大写')
if '__doc__' not in class_dict or len(class_dict['__doc__'].strip())==0: # 实现类必须有文档注释,否则抛出异常
raise TypeError('必须有文档注释')
class Test(metaclass=MyMeta):
'''
我是文档注释
'''
def __init__(self):
self.name = 'python'
def test(self):
print('test')
从上述代码我们可以得到如下结论:
只要调用类,就会依次调用类中的__new__方法和__init__方法;
__new__方法返回一个空对象,就类似一张白纸;
__init__获取__new__方法中产生的白纸在上面画不同的图案。
class Test():
def __init__(self):
self.name = 'python'
def __call__(self, *args, **kwargs): # self是Test类的对象
print(self) # <__main__.Test object at 0x000001C78CE78FD0>
print(self.name)
t = Test()
t() # python
因此我们可以得到以下结论:
对象加括号调用会调用该对象的类中定义的__call__方法;
类加括号调用会调用内置元类或自定义元类中的__call__方法,取决于类的元类是什么;
自定义元类加括号调用会内置元类中的__call__方法。
我们可以简单验证一下上述结论:
class MyMeta(type):
def __call__(self, *args, **kwargs):
print(self)
print(args)
print(kwargs)
return 'test'
class Test(metaclass=MyMeta):
def __init__(self, name, age):
self.name = name
self.age = age
# 调用Test就调用了
t = Test()
print(t)
'''
<class '__main__.Test'>
('haha', '123')
{}
test
'''
通过上述代码我们可以推断出调用Test时,会调用自定义元类中的__call__方法,并将返回值赋值给调用类产生的对象。
我们也可以通过__call__方法自定义元类来控制类的调用,也就是产生对象。
class Mymeta(type):
def __init__(self, class_name, class_bases, class_dict):
# 实现类名首字母必须大写,否则抛出异常
if not class_name.istitle():
raise NameError('类名的首字母必须大写')
# 实现创建的类必须有文档注释,否则抛出异常
if '__doc__' not in class_dict or len(class_dict['__doc__'].strip())==0:
raise TypeError('必须有文档注释')
def __new__(cls, *args, **kwargs):
return type.__new__(cls, *args, **kwargs)
def __call__(self, *args, **kwargs):
# self指的就是当前类产生的对象
people_obj = self.__new__(self)
self.__init__(people_obj,*args, **kwargs)
return people_obj
Test = MyMeta(class_name, class_bases, class_dic) # 调用的是内置元类type的__call__方法
class Test(metaclass=Mymeta):
"""
我是文档注释
"""
def __new__(cls, *args, **kwargs):
# 产生空对象--真正的对象,真正造对象的是object
return object.__new__(cls) # 这里使用type也没有问题
def __init__(self,name):
self.name = name
t = Test() # 调用的是自定义元类中的__call__方法
通过自定义元类来创建类的时候,会调用type的__call__方法,该方法内部会做三件事情:
type.__call__做的事情:
1、先调用自定义元类中的__new__方法,产生一个空对象
2、在调用自定义元类中的__init__方法,为空对象添加独有属性
3、返回一个初始化好的自定义元类的对象,就是上述的Test类
调用Test类时则会调用自定义元类MyMeta.__call__方法,同样也会做三件事:
MyMeta.__call__做的事情:
1、先调用 Test类中的__new__方法产生一个空对象
2、再调用Test 类中的__init__方法为空对象添加独有属性
3、返回一个初始化好的Test类的对象赋值给t
转载请注明:XAMPP中文组官网 » python一切皆对象- 元类详解