121 lines
3.6 KiB
Python
121 lines
3.6 KiB
Python
# -*- coding: utf-8 -*-
|
||
class Test:
|
||
pass
|
||
|
||
print(type(Test))
|
||
print(type(int))
|
||
|
||
# 结果都为<class 'type'>,type就是内置的元类,class关键字定义的所有的类以及内置的类都是由元类type实例化产生。
|
||
|
||
"""
|
||
由于在python3中没有经典类和新式类的区别,因此python3中object是所有类的基类,那内置元类type又是什么?
|
||
type是object类的类型,总结来说就是type是object的类型,同时,object又是type的基类,这句话看起来就有问题,
|
||
到底是先有type还是先有object呢?这个问题有点类似于先有鸡还是先有蛋,我们可以通过代码简单分析一下
|
||
|
||
在python3中类和类型是同一种东西,因此对象.__class__和type(对象)得到的结果是一致的,object的基类为空,
|
||
但是type的基类为object,但是object的类型又是type。
|
||
"""
|
||
print(type(object))
|
||
print(object.__class__)
|
||
|
||
print(type(type))
|
||
print(type.__class__)
|
||
|
||
print(object.__bases__)
|
||
print(type.__bases__)
|
||
|
||
# python字符串
|
||
strs = """
|
||
global name
|
||
global age
|
||
name = 'py'
|
||
age = 18
|
||
addr = 'xx'
|
||
"""
|
||
# 定义全局作用域中的名字和值
|
||
globals = {
|
||
'a': '1',
|
||
'b': '2'
|
||
}
|
||
# 定义局部作用域中的名字和值
|
||
locals = {
|
||
'x': 3,
|
||
'y': 4
|
||
}
|
||
exec(strs, globals, locals)
|
||
print(globals['name'])
|
||
print(locals)
|
||
|
||
# 了解了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')
|
||
t.test()
|
||
|
||
# 自定义元类
|
||
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('必须有文档注释')
|
||
|
||
def __call__(self, *args, **kwargs):
|
||
print(self)
|
||
print(args)
|
||
print(kwargs)
|
||
return 'test'
|
||
|
||
class Test(metaclass=MyMeta):
|
||
|
||
'''
|
||
我是文档注释
|
||
'''
|
||
|
||
def __init__(self):
|
||
self.name = 'python'
|
||
|
||
def test(self):
|
||
print('test')
|
||
t = Test() |