【py3】使用元类模拟ORM

什么是元类?

先看下列代码,对象 s 是由类Student创建的,那么Student类又是由谁创建的呢?答案就是元类type,元类就是用来创建类的类。

class Student():
    pass
s = Student()
print(s.__class__, s.__class__.__class__)

--------------------------------------------------
<class '__main__.Student'> <class 'type'>

以上代码等价于直接由type创建目标类,只需在type中传入类名,继承的类元组,类属性字典即可。注意这里的 t 代表的是类Master,而非对象。

t = type("Master", (object,), {
    
    })
print(t.__class__)
--------------------------------------------------
<class 'type'>

什么是ORM?

ORM是对象关系映射,对象通常就是指实体对象,关系通常就是指关系型数据库表,就是从代码对象映射到数据库表,即操作对象完成数据库表的操作。ORM是python后端web框架Django的核心思想,下面简单模拟下orm。

对象创建过程

python中对象的创建是分为创建和初始化两步的,首先调用__new__方法创建对象,然后调用__init__完成对象初始化。

class Student(object):
  def __new__(cls, *args, **kwargs):
    print("创建对象...")
    return super().__new__(cls)
    def __init__(self):
    print("初始化...")
    self.age = "15"
if __name__ == '__main__':
    s = Student()
--------------------------------------------------
创建对象...
初始化...

使用元类操作类

我们很少直接使用__new__方法,因为很少去操控类的创建,元类是创建类的超类,所以我们可以在元类的__new__方法中完成对目标类的操控。以下我们使用元类完成对象单例模式。


class Singleton(type):
  def __new__(mcs, class_name, class_bases, class_attrs):
    class_attrs['instance'] = None
    return type.__new__(mcs, class_name, class_bases, class_attrs)
  def __call__(cls, *args, **kwargs):
    if cls.instance is None:
      cls.instance = super(Singleton, cls).__call__(*args, **kwargs)
    return cls.instance

class Car(object, metaclass=Singleton):
    pass
if __name__ == '__main__':
    s = Car()
    t = Car()
    print(id(s), id(t))
--------------------------------------------------
31498640 31498640

使用元类实现ORM

上面我们已经看到了使用元类可以操作目标类,下面我们使用元类完成数据库表操作,先创建一个实体对象类。

class User(Model, metaclass=BaseModel):
    _table = 't_user'
    name = 'u_name'
    age = 'u_age'

使用元类收集对象映射的表名,字段名称及值。注意一个类有许多内置属性,收集属性时只收集自定义属性,其中表名我们约定使用下划线开头,优秀的框架都有自己的约定,约定优于配置。

class BaseModel(type):
    def __new__(mcs, class_name, class_bases, class_attrs):
        print(mcs, class_name, class_bases, class_attrs)
        tmp = dict()
        for k, v in class_attrs.items():
            if not str(k).startswith('_'):
                tmp[k] = v
        class_attrs['mapping'] = tmp
        class_attrs['table'] = class_attrs['_table']
        return type.__new__(mcs, class_name, class_bases, class_attrs)

我们把通用的操作抽象到一个基类中,在初始化时将传入的所有关键字参数初始化到实例属性中。这里以保存为例,收集字段名称和字段对应的数值,然后组装SQL,这样就完成了将对象操作映射到数据库表。

class Model(object):
    def __init__(self, **kwargs):
        for k, v in kwargs.items():
            setattr(self, k, v)
    def save(self):
        cloumns = []
        values = []
        for k, v in self.mapping.items():
            cloumns.append(v)
            values.append(getattr(self, k, None))
        tmp = []
        for v in values:
            if isinstance(v, str):
                tmp.append("""'%s'""" % v)
            else:
                tmp.append(str(v))
    sql = 'insert into %s (%s) values (%s)'% (self._table, ','.join(cloumns), ','.join(tmp))
    print("mapping sql: %s" % sql)

测试:

if __name__ == '__main__':
    u = User(name='码农小麦', age=25)
    u.save()
print('-'*50)
--------------------------------------------------
mapping sql: insert into t_user (u_name,u_age) values ('码农小麦',25)

猜你喜欢

转载自blog.csdn.net/weixin_43275277/article/details/113567993