大家都说python中的元类(metaclass)在真实场景中99%的几率都用不到,但是最大熵理论告诉我们,面对未知风险,我们不能报侥幸心理,我们应该平权看待所有潜在风险,不应该放过任何一处潜在风险。
So,本文主要讲讲python的元类,为了不枯燥无味,本文以一个初级ORM框架例子来讲解。
什么是ORM
Object Relational Mapping 即 对象-关系映射,在关系型数据库中就是把关系数据库的一行映射为一个对象,即一个类对应一个表,这样,你就不需要每次都用pymysql去封装每条sql了,把重复的操作交给ORM,如sqlalchemy就是一个 ORM框架
可能这个解释有点牵强,在此举个例子把,比如说你要向一个数据表user插入数据,你可能会写: 1insert into user(name, age) values('seanlee', 21)
当插入数据到其他表时比如说course时,你又要: 1insert into course(name, teacher) values('computer sicence', 'Mr.Gates')
这时你发现每次插入数据都要自己写完所有语句,且要处理完整个逻辑过程。这真的很累,于是乎,如果有一个框架能把这些复杂的工作给做了,使用者只需封装好模型类即可自动完成复杂的流程。 你的想法可能是这样的: 1
2
3
4
5
6
7
8
9
10class User(ORM):
# 定义数据表的字段
name = CharField()
age = IntField()
if __name__ == '__main__':
user = User()
user.name = "seanlee"
user.age = 21
user.save() # 调用save函数框架自动将数据插入到数据库中
好了,上面就是ORM的大致思路了,本文要实现的目标就是完成上面代码的内部逻辑。
什么是元类
元类metaclass是类的类,简单的说就是元类是用来生产类的,可以说元类是最上层的类。可能还是很难理解,举几个例子把
1. object类
平时大家定义类时可能会继承object类,大家下意识的认为object就是最上层的类了,其实不是,我们输出object的类型看看 1print(type(object)) #
可以看到object类的类型是type类型,那么type类型又是什么呢?type类型就是指type类,大家都以为type()是一个函数,其实它是一个类,当只传一个参数时它会返回参数的类型。
其实type就是python的元类,看下面程序: 1print(type(type)) #
可以看到type的类型就是type
2. 基本数据类型
python中的基本数据类型也可看为对象,它们的直属类型的类就是 type,看下面例子:
1
2
3
4
5
6print(type(type(97))) # 整型
print(type(type(97.0))) # 浮点
print(type(type("seanlee"))) # 字符串
print(type(type((9, 7)))) # 元组
print(type(type([9, 7]))) # 列表
print(type(type({"key": "val"}))) # 字典
输出结果为: 1
2
3
4
5
元类的作用
掌握了元类,你就是“类”的老大了,你可以按照你的方式去修改类,就好比你得到了Linux的root帐号,你可以为所欲为了。也好比你是微信群的群主,拥有直接拉人入群、踢人出群的权力,python中的元类也类似可以用于对类属性的增删操作。
好啦,到这里你应该理解元类的概念了
type类
从此刻起一定要更正观念type是一个类,它有三个参数: 1type(what [, bases, dict])
可见后两个参数是可选的,各参数的意义:
what: 输入的实例
bases:类的基类,传入的是元组
dict:类的属性、方法
举几个例子说明,type的用法
直接生产类
1
2
3
4User = type('User', (object, ), {'name': 'seanlee', 'age': 21})
user = User()
print(user.name) # seanlee
print(user.age) # 21
修改类属性
1
2
3
4
5
6
7
8
9
10
11
12
13class User(object):
name = None
user = User()
print(user.name) # None
def get_name(self):
return self.name
User = type('User', (object, ), {'name':'seanlee', 'get_name': get_name})
user = User()
print(user.name) # seanlee
print(user.get_name()) # seanlee
python中类的创建
如果类中没有定义metaclass,那么直接用type创建
如果用户定义了metaclass那么以metaclass的方式创建
自定义元类的创建
1
2
3
4
5
6
7
8
9
10
11
12
13class Metaclass(type):
def __new__(cls, name, bases, attrs, **kwargs):
# do something here.
print("call Metaclass")
return super(Metaclass, cls).__new__(cls, name, bases, attrs, **kwargs)
class User(metaclass=Metaclass):
# __metaclass__ = Metaclass # python2支持的方式
def __init__(self):
print("call User")
if __name__ == '__main__':
User()
输出: 1
2call Metaclass
call User
开始打造ORM
1. 使用描述器定义字段属性
如果不了解描述器,请阅读python中的描述器
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# 定义字段属性的基类Field,它的用处主要用于识别类属性中属于字段的那些属性
class Field(object):
pass
# 整型字段属性
class IntField(Field):
def __init__(self, db_column=""):
self._value = None # 表的数据
self.db_column = db_column
def __set__(self, instance, value):
if not isinstance(value, int):
raise ValueError("input should be a Integer")
self._value = value
def __get__(self, instance, owner):
return self._value
# 字符型字段属性
class CharField(Field):
def __init__(self, db_column=""):
self._value = None
self.db_column = db_column
def __get__(self, instance, owner):
return self._value
def __set__(self, instance, value):
if not isinstance(value, str):
raise ValueError("input should be a String")
self._value = value
2. 定义元类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16class MetaModel(type):
def __new__(cls, name, bases, attrs, **kwargs):
fields = {}
for key, val in attrs.items():
# 把attrs中与数据库表字段有关的列提取出来
if isinstance(val, Field):
fields[key] = val # value 直接走描述器__get__()
_meta = {}
db_table = name.lower() # 数据表名称默认取小写类名称
_meta['db_table'] = db_table
attrs['_meta'] = _meta
attrs['_fields'] = fields
# 以上过程相当于对类进行了修改
return super(MetaModel, cls).__new__(cls, name, bases, attrs, **kwargs)
3. 定义模型基类
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
38class Model(metaclass=MetaModel):
def __init__(self, *args, **kwargs):
for key, val in kwargs.items():
setattr(self, key, val)
return super(Model, self).__init__()
def save(self):
fields = []
values = []
for key, val in self._fields.items():
db_column = val.db_column
if db_column is None:
db_column = key.lower()
fields.append(db_column)
value = getattr(self, key) # 字段的值
values.append(str(value))
sql = "insert {name} ({field}) values ({value})".format(name=self._meta['db_table'],
field=','.join(fields),
value=','.join(values))
return sql
def select(self):
fields = []
where = []
for key, val in self._fields.items():
db_column = val.db_column
if db_column is None:
db_column = key.lower()
fields.append(db_column)
v = getattr(self, key, None)
if v is not None:
where.append([key, str(v)])
sql = 'select {fields} from {name} where {where}'.format(name=self._meta['db_table'],
fields=','.join(fields),
where=' and '.join(['='.join(x) for x in where]),
)
return sql
上述三步就完成了简单orm的搭建了,为了简单没封装真实的数据库操作,仅返回SQL语句而已。
4. 操作实例
1
2
3
4
5
6
7
8
9
10
11
12class User(Model):
name = CharField(db_column="name", max_length=10)
age = IntField(db_column="age", min=0, max=100)
class Meta: # 使用内部类来定义数据表的其他属性
db_table = "db_user"
if __name__ == '__main__':
user = User(name='seanlee', age=15)
user.name = 'sean'
print(user.save())
print(user.select())
输出 1
2insert user (name,age) values (sean,15)
select name,age from user where name=sean and age=15