yield-bytes

沉淀、分享与无限进步

深入解析Python元类作用

  python的元类使用场景一般在大型框架里面,例如Django的ORM框架、基于python实现的高级设计模式,元类的这部分内容相对晦涩,但也是作为python非常核心的知识点,通过解析其机制,有利于阅读和学习优秀中间件源代码的设计逻辑,在面向对象设计的重要性不言而喻。本博客后面的内容将会给出较为复杂的设计模式的文章,里面会出现较多的元类编程,因此有必要单独开一篇文章讨论python元类,相关内容将参考Stack Overflow上一篇很受欢迎的关于python metaclasses的文章:what-are-metaclasses-in-python

1、python的class对象

1.1 python的class也是一种object

”在python的世界里,一切皆对象(object)“,这句话经常出现在很多python书籍中有关”面向对象或者类“文章里。如果你要深入python,首先面向对象的思维和面向对象的编程经历较为丰富。掌握对类的理解和运用,是理解元类的重要基础。
1.1 类即对象

1
2
3
4
5
6
In [1]: class Foo(object):
...: pass
...:
In [2]: my_instance=Foo()
In [3]: print(my_instance)
<__main__.Foo object at 0x108561b00>

这里创建了一个名为Foo的类,打印它的实例,可以看到该实例是一个Foo object 存放在内存地址:0x108561b00
这里只是说明Foo类的实例是object,怎么确认Foo是一个object呢?
两种方式可以回答:
方式一:在定义阶段:Foo(object),Foo这个类继承object,所以Foo是object
方式二:
1
2
In [8]: isinstance(Foo,object)
Out[8]: True

既然Foo是一个object,那么对于该object则可以扩展其功能:

  • 可赋值给变量
  • 可被复制

    • 可添加属性
    • 可将其当作函数参数传递
    • 当然也可绑定新的类或者对象
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    In [13]: NewFoo=Foo
    In [14]: print(NewFoo)
    <class '__main__.Foo'>


    In [16]: CloneFoo=copy.deepcopy(Foo)
    In [17]: print(CloneFoo)
    <class '__main__.Foo'>


    In [20]: Foo.new_attr='bar'
    In [21]: Foo.new_attr
    Out[21]: 'bar


    In [22]: def myfunc(obj):
    ...: print(obj.__name__)
    ...:
    In [23]: myfunc(Foo)
    Foo


    In [24]: class NewFoo(object):
    ...: pass
    ...:
    In [25]: Foo.x=NewFoo
    In [26]: Foo.x
    Out[26]: __main__.NewFoo

    总之,只要拿到一个object,你可以对其扩展任意你想得到效果

1.2 动态创建类

什么是动态创建类?只有运行这个程序后,通过判断给定参数来决定创建的是类A还是类B,而不是给程序写为固定生产类A。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
In [28]: def choose_class(which):
...: if which =='Foo':
...: class Foo(object):
...: pass
...: return Foo
...: elif which == 'Bar':
...: class Bar(object):
...: pass
...: return Bar
...:
...:

In [29]: myclass=choose_class('Bar')
In [32]: print(myclass.__name__)
Bar

前面提到,既然Foo创建一个实例就是一个对象,把这个逻辑放在Foo身上:既然(某某某)创建一个对象就是一个Foo类,这个某某某是什么?可以做什么?
这个某某某就是type这个内建函数(函数也是一个对象),用type也可以像上面一样动态的创建一个类,用法:
1
2
3
type(要创建的类名,该类的所有父类名字组成的元组(若无父类,则为空元组),要创建该类需要用到入参:属性的字典)
一般写成:
type(class_name,class_bases,class_dict)

经典方式一般如下:
1
2
3
4
5
6
7
8
9
class Person(object):
car='Model 3'
def __init__(self,name,age):
self.name=age
self.age=age

def info(self):
print('my name is {} and {} years old'.format(self.name,self.age))


使用type函数动态创建以上Person类的过程:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 因为Person继承object,所以type的第二个位置参数为(object,),Person类有三个属性因此class_dict为{'car':car,'__init__':__init__,'info':info})
car = 'Model 3'
def __init__(self,name,age):
self.name = name
self.age = age

def info(self):
print(self.name,self.age)

Person = type('Person',(object,),{'car':car,'__init__':__init__,'info':info})


In [3]: Person.__dict__
Out[3]:
mappingproxy({'car': 'Model 3',
'__init__': <function __main__.__init__(self, name, age)>,
'info': <function __main__.info(self)>,
'__module__': '__main__',
'__dict__': <attribute '__dict__' of 'Person' objects>,
'__weakref__': <attribute '__weakref__' of 'Person' objects>,
'__doc__': None})
In [5]: person = Person('Watt',20)

In [6]: person
Out[6]: <__main__.Person at 0x10896f9e8>

type创建完整Person类!本章内容主要通过类的创建,因此type这个函数并用其实现动态创建类,为元类这个话题做了铺垫,通过以上type创建的实例推出,python创建类必须要具备以下三个参数:

  • 1、类名class_name
  • 2、继承关系class_bases
  • 3、类的名称空间class_dict
    这三个参数是揭开元类是如何改变类的秘密。

2 、Python的metaclass元类

2.1 认识type

前面的内容已经说明Python中的类也是对象,那么metaclass元类(元类自己也是对象)就是用来创建这些类的类,例如可以这样理解:

1
2
MyClass = MetaClass()    #元类创建了类
MyObject = MyClass() #被元类创建的类后,用它创建了实例

在上一节内容,type可创建MyClass类:
MyClass = type('MyClass', (), {})
MyClass是type()这个特殊类的一个实例,只不过这个实例直接就是类。
以上的逻辑主要说明一件事:type这个特殊类,就是python的一个元类,type是Python在背后用来创建所有类的元类,这句话如何理解?
首先,还是那句熟悉的话:在python的世界里,一切皆对象(object),包括各类数据结构、函数、类以及元类,它们都来源于一个“创物者”,这个强大的创物者这就是type元类。
查看每种对象的__class__属性:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
In [55]: num=10
In [56]: num.__class__
Out[56]: int

In [58]: alist=['a','c']
In [59]: alist.__class__
Out[59]: list


In [60]: def foo():
...: pass
...:

In [61]: foo.__class__
Out[61]: function


In [62]: class Bar(object):
...: pass
...:

In [64]: b=Bar()
In [65]: b.__class__
Out[65]: __main__.Bar

说明每个对象都是某种类,
那么,一个__class__.__class__又是属于哪种类呢?
1
2
3
4
5
6
7
8
9
10
11
In [69]: num.__class__.__class__
Out[69]: type

In [70]: foo.__class__.__class__
Out[70]: type

In [71]: alist.__class__.__class__
Out[71]: type

In [72]: b.__class__.__class__
Out[72]: type

在继续往“创物者”方向靠近,发现最后都是type:
1
2
3
4
5
In [74]: foo.__class__.__class__.__class__
Out[74]: type

In [75]: foo.__class__.__class__.__class__.__class__
Out[75]: type

以上说明:python的各类数据结构、函数、类以及元类,它们都来源于一个“创物者”,这个强大的创物者这就是type元类。

2.2 认识__metaclass__属性

在python的元类的设计中,通常会出现__metaclass__属性,一般用法如下:

1
2
3
4
5
class Foo(object):   #python2版本的写法
__metaclass__ = something…

class Foo(metaclass=something): #python3版本的写法
__metaclass__ = something…

当一个类的内部属性定义了__metaclass__属性,说明这个类将由某个元类来创建,当Foo类一旦被调用,因为设计类时可能有继承关系,因此会出现属性搜索过程:
1)Foo的定义里面有__metaclass__这个属性?如果有,解释器在内存中通过something...这个元类创建一个名字为Foo的类(对象)
2)如果在Foo的作用域内未找到__metaclass__属性,则继续在父类中寻找,若在父类找到,则用something...这个元类创建一个名字为Foo的类(对象)。
3)如果任何父类中都找不到__metaclass__属性,它就会在模块层次中去寻找__metaclass__,若找到,则用something...这个元类创建一个名字为Foo的类(对象)。
4)如果还是找不到__metaclass__,解释器最终使用内置的type来创建这个Foo类对象。

从上面过程可知,既然找到something...这个元类后它就可以创建类,说明它与type这个终极元类作用一样:都是用来创建类。
所以可推出:__metaclass__指向某个跟type功能相仿的元类———任何封装type的元类、继承type的子类、type本身

下面用元类实现的redis连接单例来感受下以上的逻辑:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class RedisMetaSingleton(type):

def __init__(cls,class_name,class_bases,class_dict):
"""元类做初始化"""
#super(RedisMetaSingleton, cls).__init__(class_name, class_bases, class_dict) python2写法
super().__init__(class_name, class_bases, class_dict) # python3写法
cls._instance =None

def __call__(cls,host,port,db):
"""call调用即完成类的实例化,用类的入参创建redis连接实例"""
if not cls._instance:
# cls._instance = super(RedisMetaSingleton, cls).__call__(host,port,db) python2写法
cls._instance = super().__call__(host,port,db)# python3写法
return cls._instance

class RedisSingleton(metaclass=RedisMetaSingleton):
"redis操作专用类"
def __init__(self,host,port,db):
self.host=host
self.port=port
self.db=db
def conn(self):
pass

测试其实例是否为单例:

1
2
3
4
5
6
7
In [37]: r1=RedisSingleton('182.0.0.10','6379',0)
In [38]: r1
Out[38]: <__main__.RedisSingleton at 0x10fdc6080>

In [39]: r2=RedisSingleton('182.0.0.10','6379',0)
In [40]: r1 is r2
Out[40]: True

将单例逻辑放在定义元类这里,其他redis常用方法则放在子类实现。此外,该测试用例需要注意的两点:
1)RedisMetaSingleton的__init____call__第一个参数为cls,表示元类要创建的”类对象“,因此用cls而不是self。元类至于类对象(mataclass==>class object),就像类至于实例(class==>instance),反复理解该句。
2)__init__(cls,class_name,class_bases,class_dict),第2个到4个参数,其实就是type元类创建类的所需参数:
type(类名,父类元组(若无父类,则为空元组),类属性或内部方法的字典)
3)由于RedisMetaSingleton继承type,那么super(RedisMetaSingleton, cls)经过搜索后,父类就是type,因此
A: super(RedisMetaSingleton, cls).__init__(class_name, class_bases, class_dict)的初始化就等价于
type.__init__(class_name, class_bases, class_dict)的初始化
B:super(RedisMetaSingleton, cls).__call__(host,port,db)创建类对象就等价于type.__call__(host,port,db)创建类对象
这就说明RedisSingleton指定由RedisMetaSingleton来创建,在RedisMetaSingleton内部最后交由type.__init__初始化,证明过程如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
In [119]: class RedisMetaSingleton(type):
...: """在元类层面实现单例"""
...: def __init__(cls,class_name,class_bases,class_dict):
...: super(RedisMetaSingleton, cls).__init__(class_name, class_bases, class_dict)
...: print('class_name:{} class_bases:{} class_dict:{}'.format(class_name,class_bases,class_dict))
...: cls.cls_object =None
...:
...: def __call__(cls,host,port,db):
...: if not cls.cls_object:
...: cls.cls_object = super(RedisMetaSingleton, cls).__call__(host,port,db)
...: return cls.cls_object
...:
...: class RedisSingleton(metaclass=RedisMetaSingleton):
...: "redis操作专用类"
...: def __init__(self,host,port,db):
...: self.host=host
...: self.port=port
...: self.db=db
...: def conn(self):
...: pass
...:


当以上代码在ipython解释器敲下去后,解释器对RedisMetaSingleton做了__init__初始化工作,故可得到以下打印信息
1
2
3
class_name:RedisSingleton  # 类名
class_bases:() # 父类元组
class_dict:{'__module__': '__main__', '__qualname__': 'RedisSingleton', '__doc__': 'redis操作专用类', '__init__': <function RedisSingleton.__init__ at 0x10ffa0158>, 'conn': <function RedisSingleton.conn at 0x10ffa00d0>} # 要创建类的所有属性字典

这三个参数就是type创建类的所需的参数:
type(类名,父类元组(若无父类,则为空元组),类属性或内部方法的字典)

以上内容略显复杂:归结起来,只要一个普通类指定需要元类创建,那么最终一定是由type这个终极元类来创建。

2.3 自定义元类

对元类的构建和原理有一定认识后,那么可通过元类定制普通类,真正站在创物者的上帝视野来创建普通类。
现在有这样一个需求,要求创建的普通类的属性满足以下条件:
对于开头不是__的属性,都要大写,例如getname(self),在元类创建该普通类后都会被改为GETNAME(self)
开头为__的属性,大小写保持不变。
从type创建普通类的“公式“可知:type(classname,classbases,classdict),classdict就是放置了普通类属性或内部方法的字典),故只需要对其修改后,再重新传入type即可实现,需要基于type的__new__方法实现,type当然有`__new方法,因为type是元类,也是类。(元类必然有__new方法,它创建的普通类例如Person、RedisConn才有这个内建的__new
方法。) 具体实现:

1
2
3
4
5
6
7
8
9
10
11
class UpperAttrMetaclass(type):
# 在这里,被创建的对象是类,因此第一个参数为cls,而不是类实例化的self,且需重写__new__方法
def __new__(cls, class_name,class_bases,class_dict):
uppercase_attr = {}
for name, val in class_dict.items():
if not name.startswith('__'):
uppercase_attr[name.upper()] = val
else:
uppercase_attr[name] = val
# 用uppercase_attr替换了原class_dict,再传入到type,由type创建类,实现了自定义创建类的目标
return type(class_name,class_bases, uppercase_attr)
考虑到return type(class_name,class_bases, uppercase_attr)`的写法不是pythone的OOP的写法(不够高级、抽象),因此又转化为以下OOP写法:

1
2
3
4
5
6
7
class UpperAttrMetaclass(type):
# 在这里,被创建的对象是类,因此第一个参数为cls,而不是实例self,且需重写__new__方法
def __new__(cls, class_name,class_bases,class_dict):
attrs = ((name, value) for name, value in class_dict.items() if not name.startswith('__'))
uppercase_attr = dict((name.upper(), value) for name, value in attrs)
# 用uppercase_attr替换了原class_dict,再传入type,由type创建类,实现了自定义创建类的目标
return type.__new__(cls,class_name,class_bases, uppercase_attr)

以上OOP风格在知名的python框架中到处可见!此外,我们知道通过super(UpperAttrMetaclass,cls)可以搜索到父类type,因此开发者会习惯写成以下形式:

1
2
3
4
5
6
class UpperAttrMetaclass(type):
def __new__(cls, class_name,class_bases,class_dict):
attrs = ((name, value) for name, value in class_dict.items() if not name.startswith('__'))
uppercase_attr = dict((name.upper(), value) for name, value in attrs)
# return super(UpperAttrMetaclass,cls).__new__(cls,class_name,class_bases, uppercase_attr)
return super().__new__(cls,class_name,class_bases, uppercase_attr) # python3的写法

定义一个普通类测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
In [124]: class Person(metaclass=UpperAttrMetaclass):
...: def __init__(self,name,age):
...: self.name=name
...: self.age=age
...: def get_name(self):
...: print('name is:',self.name)
...: def get_age(self):
...: print('age is:',self.age)
...:

In [125]: Person.__dict__
Out[125]:
mappingproxy({'GET_NAME': <function __main__.Person.get_name(self)>,
'GET_AGE': <function __main__.Person.get_age(self)>,
'__module__': '__main__',
'__dict__': <attribute '__dict__' of 'Person' objects>,
'__weakref__': <attribute '__weakref__' of 'Person' objects>,
'__doc__': None})


当普通类Person定义后,解释器已经用元类UpperAttrMetaclass创建了Person普通类,其get_name和get_age方法名都被改为大写:GET_NAME和GET_AGE,其他双下划线的的方法名字保持不变。

此外,metaclass不局限于类的调用,也可以在任何对象内部调用,例如函数内部调用,例如以下一个模块upper_attr_by_func.py:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def upper_attr(class_name,class_bases,class_dict):
uppercase_attr = {}
for name, val in class_dict.items():
if not name.startswith('__'):
uppercase_attr[name.upper()] = val
else:
uppercase_attr[name] = val

return type(class_name,class_bases,class_dict)

__metaclass__ = upper_attr # 该元类只能作用在本模块的所有类,对其他模块a.py、b.py无影响。

class Person(metaclass=UpperAttrMetaclass):
def __init__(self,name,age):
self.name=name
self.age=age
def get_name(self):
print('name is:',self.name)

def get_age(self):
print('age is:',self.age)

但需要注意的是:这种方式,元类的作用域将受到限制,仅能影响本模块upper_attr_by_func.py的所有类,对其他模块的类不产生作用。

综上,可总结元类定制普通类的创建一般如下过程:

  • 拦截一个普通类,一般在会使用__new__,__init__ 和 __call__,这些方法的内部可以放入对普通类进行不同定制的代码逻辑,其中:
    A、__new____init__方法用于控制类的行为
    B、 __call__方法用于控制类实例化的行为(
  • 修改普通类,一般是指修改type(classname,classbases,class_dict)里面的三个参数,尤其对class_dict修改频繁,例如要求class_dict里面的必须要有`__doc`属性,甚至针对父类元组class_bases来操作其继承关系。
  • 通过return super().__new__(cls,class_name,custom_bases, custom_class_dict)创建并返回普通类

元类一般用于复杂的框架上改变类的行为,对于普通简单的类,还有其他两种手段用来改变类:

  • monkey patching
  • 类装饰器
    按Stack Overflow上的”建议“:
    如果需要改变类,99%的情况下使用这两种方法,但其实98%的情况你根本不需要改变类。所以你看到很多较为简单的python轮子,一般是几个普通类就可以完成,根本无需动用元类来构建普通类。

3、元类定制普通类的示例——myORM

本节内容参考了廖雪峰的文章,但其文章有很多关键的语句并无做更细致的说明,本节内容会在重要的元类实现逻辑上给出更详细的文字说明。
这里将实现一个轻量ORM——myORM:
在架构层面(不面向用户)

  • 架构定义了一个元类ModelMetaClass,用于拦截和修改普通类User定义阶段的class_dict属性字典,并用改造后的class_dict传入type来创建普通类User对象。
  • 架构定义了一个Model类,用于把字段属性名和字段值封装在拼接的SQL语句,主要负责与数据库的增删查改。
  • 架构定义了一个基本字段类Field:包含字段名和字段类型

在用户层面(面向用户,用户可自行定义各种模型)

  • 用户定义一个整数字段类IntField,用于存放整型类型数据,例如id号,age
  • 用户定义一个字符串字段类CharField,用于存放字符类型数据,例如name,email
  • 创建一个User模型的一条行记录
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157

class ModelMetaClass(type):
"""
例如定义User普通类,如下:
class User(Model):
id=IntField('user_id')
name=CharField('user_name')
email=CharField('email')
password=CharField('password')

那么元类ModelMetaClass捕获到的属性字典为:
class_dict={
'__module__': '__main__',
'__qualname__': 'User',
'id':IntField<user_id,bigint>,
'name':CharField<'user_name',varchar(100)>,
'email':CharField<'email',varchar(100)),
'password':CharField<'password',varchar(100)>
}
"""

def __new__(cls,class_name,class_bases,class_dict):
if class_name == "Model":
# 如果创建的普通类为Model,不修改该类,直接创建即可
return type.__new__(cls,class_name,class_bases,class_dict)
print('在ModelMetaClass元类捕获到普通类的属性字典:\n',class_dict)

# 用于存放字段类型的属性
fields_dict=dict()

for attr_name,attr in class_dict.items():
# 因为普通类属性字典还有__doc__,__qualname__等属性,因此要过滤出属于Field类型属性
if isinstance(attr,Field):
# 打印结果:attr_name is "id",field object is "<class '__main__.IntField'>"等字段信息
print('attr_name is "{}",field object is "{}"'.format(attr_name,type(attr)))
fields_dict[attr_name]=attr


for field_name in fields_dict.keys():
"""
把Field类型的属性在原属性字典剔除:
u=User(name='Wott')
print(u.name)# 打印结果Wott

若不在原字典删除,那么u.name的值为:CharField<email,varchar(100)>
这个值是元类创建User类的属性值,它会覆盖了值为'Wott'的u.name实例属性,显然不符合实际情况。
"""
class_dict.pop(field_name)

# 在原属性字典里增加一个私有属性(字典类型),这个私有属性保存了要创建字段的信息
class_dict['__fields_dict__']=fields_dict



"""
获取自定义模型中,指定Meta信息的数据库表名
class User(Model):
...
class Meta:
db_table=USER_T
"""
attr_meta=class_dict.get('Meta',None)
print('User模型定义的Meta属性:',attr_meta) # Meta: <class '__main__.User.Meta'>
meta_table_name=getattr(attr_meta,'db_table',None)
print('User模型在Meta指定的数据库表名为:',meta_table_name) # User模型在Meta指定的数据库表名为:: USER_T
if attr_meta and meta_table_name:
table=meta_table_name
del class_dict['Meta']
else:
table=class_name # 若User模型没指定Meta中的数据库表名,则默认用User模型类名作为数据库表名
class_dict['__table_name__']=table

# 把原Meta属性变成私有属性,这样创建出来的类更具OOP风格
class_dict['__Meta__']=attr_meta
# 以上完成对普通User模型的属性字典改造后,再重新把它传入到type,从而元类ModelMetaClass完成拦截=>定制=>创建普通类的过程。
return type.__new__(cls,class_name,class_bases,class_dict)


class Model(dict,metaclass=ModelMetaClass):
"""
1、Model类继承dict,目的是为了满足ORM中使用字典赋值和取值的方式例如
创建u=User(id=1,name='Wott',email='11@11.com',password='1213')
2、Model内部通过拼接普通类的字段属性信息,封装了原生sql语句,例如save(),filter(),update()等方法
"""
def __init__(self, **kwargs):
super().__init__(**kwargs)

def __getattr__(self,attr_name):
"""
重写内建getattr方法,可实现类似u.name这种点号获取属性值得方式,用起来更具ORM风格
"""
try:
return self[attr_name]
except KeyError:
raise AttributeError("Model object has no attribute {}".format(attr_name))

def __setattr__(self,col_name,col_value):
"""
重写setattr方法,可实现类似u.name="Foo"这种通过点号设置属性值的方式,用起来更具ORM风格
"""
self[col_name]=col_value

def save(self):
# 字段名列表
column_name_list=[]
place_holder_list=[]
# 字段值列表
column_value_list=[]
fields_dict=self.__fields_dict__

# 存放字段的字典,key就是字段名,放在字段名列表,value就是字段值,放在字段值列表,两个列表用于拼接sql语句
for attr_name,attr in fields_dict.items():
# 打印为:attr_name==>id,attr==><class '__main__.IntField'>,attr.col_name==>user_id
column_name_list.append(attr.col_name)
place_holder_list.append('%s')
print(self) # Model Dict:{'id': 1, 'name': 'Foo', 'email': '11@11.com', 'password': 'Pa33Wood'}
column_value_list.append(self[attr_name])
# 或者column_value_list.append(getattr(self,attr_name))

sql = 'insert into %s (%s) values (%s)' % (self.__table_name__, ','.join(column_name_list), ','.join(place_holder_list))
print('SQL语句:',sql)
print('SQL的入参值列表:',column_value_list)
# 连接mysql数据库后,使用cur.execute(sql,column_value_list)即可存入数据

def __str__(self):
return 'Model type:{}'.format(super().__str__())


class Field(object):
def __init__(self,col_name,col_type):
self.col_name=col_name # 字段名
self.col_type=col_type # 字段类型

def __str__(self):
"""
例如将会打印以下格式:
'id': IntField<user_id,bigint>等
"""
return "{}<{},{}>".format(self.__class__.__name__,self.col_name,self.col_type)

__repr__=__str__

class CharField(Field):
"""
定义字符型字段,默认可变字符类型长度为100
"""
def __init__(self, col_name, max_length=100):
varchar_type="varchar({})".format(max_length)
super().__init__(col_name, varchar_type)


class IntField(Field):
"""
定义整型字段
"""
def __init__(self, col_name, col_type="bigint"):
super().__init__(col_name, col_type)

ModelMetaClass负责顶层设计(改造),用户创建所有的普通类如User、Article、Department等,都会被该元类重设设计(改造它们的class_dict)后再创建出这些普通类。

用户定义了一个User模型,有四个字段,并指定创建为表名为USER_T

1
2
3
4
5
6
7
8
9
10
11
12
13
class User(Model):
id=IntField('user_id')
name=CharField('user_name')
email=CharField('email',max_length=200)
password=CharField('password')

class Meta:
"""
自定义数据库表名,这里虽然Meta定义为类,
但在元类ModelMetaClass的视角来看,它是一个属性,放在class_dict里面
"""
db_table='USER_T'


当用户定义完以上的普通类User后,Python解释器首先在当前类User的定义中查找metaclass,显然当前上下文环境没有找到,则继续在父类Model中查找metaclass,发现Model定义了metaclass=ModelMetaClass,故直接交由ModelMetaclass来创建该普通的User类。

用户创建了User实例并尝试向db插入该条数据

1
2
3
4
5
u=User(id=1,name='Wott',email='11@11.com',password='1213') # Model继承dict,因此Model子类User当然可用字典创建方式来创建实例
u['name']='Foo'# Model继承dict,因此Model子类User当然可使用字典方式赋值
u.password='Pa33Wood'# Model内部定义__setattr__方法,故可用点号给属性赋值
print(u.email) # Model内部定义__getattr__方法,故可用点号取属性值
u.save()

以上代码各个位置上的print输出结果如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
第一句print输出:
在ModelMetaClass元类捕获到普通类的属性字典:
{'__module__': '__main__', '__qualname__': 'User', 'id': IntField<user_id,bigint>, 'name': CharField<user_name,varchar(100)>, 'email': CharField<email,varchar(200)>, 'password': CharField<password,varchar(100)>, 'Meta': <class '__main__.User.Meta'>}

第二句print输出:
attr_name is "id",field object is "<class '__main__.IntField'>"
attr_name is "name",field object is "<class '__main__.CharField'>"
attr_name is "email",field object is "<class '__main__.CharField'>"
attr_name is "password",field object is "<class '__main__.CharField'>"

第三句print输出:
User模型定义的Meta属性: <class '__main__.User.Meta'>

第四句print输出:
User模型在Meta指定的数据库表名为: USER_T

第五句print输出:
11@11.com

第六句print输出:
Model type:{'id': 1, 'name': 'Foo', 'email': '11@11.com', 'password': 'Pa33Wood'}
Model type:{'id': 1, 'name': 'Foo', 'email': '11@11.com', 'password': 'Pa33Wood'}
Model type:{'id': 1, 'name': 'Foo', 'email': '11@11.com', 'password': 'Pa33Wood'}
Model type:{'id': 1, 'name': 'Foo', 'email': '11@11.com', 'password': 'Pa33Wood'}

第七句print输出:
SQL语句: insert into USER_T (user_id,user_name,email,password) values (%s,%s,%s,%s)

第八句print输出:
SQL的入参值列表: [1, 'Foo', '11@11.com', 'Pa33Wood']