Python元类metaclass
Python面向对象(new-style class)
Python一门面向对象语言,Python中一切都是对象,每个对象都有一个标识,一个类型和一个值[1]。
而且Python中一切类都是object的子类,也就是说一切对象都是object的实例。
最后Python中一切类的类都是type的子类,也就是说一切类都是type的实例。
元类metaclass指的就是class的class。
Python中Class的实现
Python中type函数即可以返回一个对象的类型,也可以用来创建出一个新的类型。
C = type("C", (object, ), {"x": 1})
assert isinstance(C, type)
assert isinstance(C(), C)
assert C.__name__ == "C"
assert C.__bases__ == (object, )
assert C.__dict__["x"] == 1
事实上Python在实现的时候,就是把一个自定义类的结构体定义为一个函数,然后直接运行函数获得返回的局部变量字典。使用类名,基类列表和局部变量字典定义一个类。
# python 2.7中执行,其他版本可能略有差异。
In [1]: import dis
In [2]: code = compile("""class C(object):x=1""", "<stdin>", "exec")
In [3]: dis.dis(code)
1 0 LOAD_CONST 0 ('C') # 类名
3 LOAD_NAME 0 (int)
6 LOAD_NAME 1 (object)
9 BUILD_TUPLE 2 # 使用之前压入的两个类型构造基类列表
12 LOAD_CONST 1 (<code object C at 0x105fc93b0, file "<stdin>", line 1>)
15 MAKE_FUNCTION 0
18 CALL_FUNCTION 0 # 运行函数获得属性字典
21 BUILD_CLASS # 生成定义的类
22 STORE_NAME 2 (C) # 赋值给变量C
25 LOAD_CONST 2 (None)
28 RETURN_VALUE
In [4]: dis.dis(code.co_consts[1])
1 0 LOAD_NAME 0 (__name__)
3 STORE_NAME 1 (__module__)
6 LOAD_CONST 0 (1)
9 STORE_NAME 2 (x) # 定义局部变量x
12 LOAD_LOCALS # 获取当前函数的局部变量字典
13 RETURN_VALUE # 返回局部变量字典
metaclass
Python在BUILD_CLASS
时默认会用type去创建类,不过我们可以通过定义metaclass修改这一行为。
Python2中通过定义属性__metaclass__
定义元类,Python3中可以使用新的class定义语法定义元类。Python的兼容性库six定义了函数with_metaclass
来定义元类。
class Meta(type):
pass
class Python2(object):
__metaclass__ = Meta
class Python3(object, metaclass=Meta):
pass
from six import with_metaclass
class MyClass(with_metaclass(Meta, object)):
pass
当定义了metaclass时,Python就会去调用metaclass(name, bases, namespace, **kwds)
去创建一个类。
使用metaclass的主要目的在于通过拦截类的创建来修改类。
应用
ORM的通常会用metaclass实现,ORM会把数据库中的表映射为对象。使用metaclass就可以通过定义类来描述表的结构,然后通过metaclass中的钩子代码将所有表信息收集。
因为在类中定义表中每个字段在数据库中的存储形式,在使用实例的时候又需要使用从数据库中获取到的值,所以在实现的时候都会将类定义的Field经过一层描述器的包装。
下面是一个简化之后的peewee的Model代码。
class FieldDescriptor(object): # 包装Field的描述器
def __init__(self, field):
self.field = field
self.att_name = self.field.name
def __get__(self, instance, instance_type = None):
if instance is not None:
return instance._data.get(self.att_name)
return self.field
def __set__(self, instance, value):
instance._data[self.att_name] = value
class Field(object): # Field类,通常会根据数据库中的类型定义Field的各种子类
def add_to_class(self, model_class, name):
self.name = name
model_class._fields.append(self)
setattr(model_class, name, FieldDescriptor(self))
class BaseModel(type): # Model的元类
inheritable = set([])
def __new__(cls, name, bases, attrs):
cls = super(BaseModel, cls).__new__(cls, name, bases, attrs)
if name == "Model":
return cls # 只在定义Model的子类时,才会是定义ORM模型
cls._fields = []
cls._data = None
cls._name = name.lower() # 定义表名,一般根据模型名生成,或者在Meta中定义
for name, attr in cls.__dict__.items():
if isinstance(attr, Field):
attr.add_to_class(cls, name) # 将Field定义修改为描述器
return cls
class Model(object):
__metaclass__ = BaseModel
def __init__(self, *args, **kwargs):
self._data = {} # 在self._data中保存字段数据
for k, v in kwargs.items():
setattr(self, k, v)
@classmethod
def select(cls):
fields= ", ".join(field.name for field in cls._fields)
sql = "SELECT ({fields}) FROM {table_name}".format(table_name=cls._name, fields=fields)
for data in cls.Meta.db.execute(sql):
yield cls(**{field.name:value for field, value in zip(cls._fields, data)})
if __name__ == "__main__":
import sqlite3
class User(Model):
name = Field()
class Meta(object):
db = sqlite3.Connection("user.db")
for user in User.select():
print(user.name)