python中通过元类(TYPE)简单实现对象关系映射(ORM)

ORM是创建一个实例对象,用创建他的类名当做数据表名,用创建他的类属性对应数据表的字段,不需要在自己写复杂的sql语句,而是通过对实例对象的操作时,能让代码自动帮我们整理为对应的sql语句。

class User(父类):
    uid = ("uid", "int unsigned")
    name = ("username", "varchar(20)")
    password = ("password", "varchar(20)")
    ...省略...
数据库
uid
username
password
     
     

类似下图创建一个实例对象,把数据库的数据以参数入

u = User(nid=12345, name="laowang",password="123321")

通过调用某个方法,ORM自动帮我们整理为下面代码并执行:

insert into (uid,name,password) value (12345,"laowang","123321")

从而大大简化我们的工作,减少出错率!下面是完整代码(通过metaclass可以指定我们需要继承的元类):

class Mode_type(type):
    def __new__(cls, name, bases, attrs)
        mappings = dict()
        for k, v in attrs.items():
            if isinstanse(v, tuple):
                mappings[k] = v
    
        # 找到新字典接收完attrs的数据后,删掉attrs里的数据
        for k in mappings.keys[]:
            attrs.pop(k)
        
        # 将之前新字典保存的数据表的信息保存在attrs中
        attrs["__mappings__"] = mappings
        # name指向新创建的实例对象名,相当于保存数据表的名称
        attrs["__table__"] = name    
        return type.__new__(cls, name, bases, attrs)


class User(mateclass = Mode_type):
    uid = ("uid", "int unsigned")
    name = ("username", "varchar(20)")
    password = ("password", "varchar(20)")
    #  指定元类后,以上的类属性就不在类中,而是在__mapping__指定的字典中仓储
    # 类似于
    # __mapping__ = {
    #        uid :("uid", "int unsigned")
    #        name: ("username", "varchar(20)")
    #        password :("password", "varchar(20)")
    # }
    # __table__ = "User"

    def __init__(self, **kwargs):
        # 取出字典里面的值
        for name, value in kwargs.items():
            setattr(self, name, value)
            # 这里不能用self.name = value ,这样只会让实例对象拥有name这个属性,而不是name形参背后真正替换的属性,所以用setattr

    def save()
        # 数据表表头字段
        fields = []
        # 数据表字段对应的数据
        args = []
        for k, v in self.__mappings__.items():
            field.append(v[0])
            args.append(getattr(self, k, None))
        args_temp = list()
        # 区分参数的类型,防止写入数据表后报错
        for temp in args:
            if isinstence(temp, int):
                args_temp.append(str(temp))  # 类似temp = “123456”
            elif isinstence(temp, str):
                args_temp.append("""%s""" % temp)  # 类似temp = """'123321'"""
        sql = "insert into %s (%s) value (%s)" % (self.__table__, ",".join(fields), ",".join(args_temp))
        #  下面就可以执行mysql的操作,只是说ORM,所以我只打印了这句话    
        print(sql)


u = User(uid = 123456, name="laowang", password = "123321")
u.save()

需要注意的点:

  • Metaclass的父类:Metaclass是类的模板,所以必须从`type`类型继承:
  • 选择__new__函数作为实现"修改类"的函数
    • 函数__new__(cls, name,bases,attrs)中,"cls"类似于类中其他函数的self参数,例如__init__(self),只不过self代表创建的对象,而cls代表类本身(__init__作为实例初始化的函数,需要把实例本身作为参数传进去,这样我们才能保证被修改的是实例;同理,__new__函数需要把类本身作为参数传进去,才能保证被初始化的是当前类); name代表类的名称;bases代表当前类的父类集合;attrs代表当前类的属性,是狭义上属性和方法的集合,可以用字典dict的方式传入
    • 其实我们看下用type创建一个类就很好理解这些参数了():
      u = type('User', (object,),{uid:("uid", "int unsigned"),name :("username", "varchar(20)"),password:("password", "varchar(20)"})
    • 对__new__的定义def __new__(cls, name,bases,attrs),实际上,“new”方法在Python中是真正的构造方法(创建并返回实例),通过这个方法可以产生一个”cls”对应的实例对象所以说”new”方法一定要有返回,要把创建的实例对象返回回去。在此,我们把对类的修改放到__new__方法中,然后返回修改过后的实例对象。另外,很简单的道理,选择type.__new__函数作为return的值,是因为我们的Mode_type继承自type,因此应该返回type的__new__函数创建的对象。

猜你喜欢

转载自www.cnblogs.com/lzb888/p/11145901.html