Python用元类向数据库插入数据

1、type元类

python中一切皆对象,同样,类也是一种对象,并且可以被创建和管理,先使用代码来看一下类来自哪里

name = '李云龙'
print(name.__class__)
print(name.__class__.__class__)
print(name.__class__.__class__.__class__)
print(name.__class__.__class__.__class__.__class__)
print(type.__class__)

结果如下:

<class 'str'>
<class 'type'>
<class 'type'>
<class 'type'>
<class 'type'>

首先声明一下,在python3中,统一使用新式类,类与类型其实是一个概念。
我们日常定义的变量,如代码中的name字符串变量,实质上是str类的实例,包括int以及list等,都是python已经写好的内置类,因此,我们在定义变量的时候,都是在做类的实例化操作。
所以我们可以直接对name变量取__class__方法,查看它所属的类名,从代码中可以看到,其所属str类。
那么str类的又是谁的实例呢?我们也可以使用__class__方法查看,结果为type类,继续往后取,发现无论取多少,结果都是一样。
同样,对type再用一次__class__方法,结果也没有变化,综上所述,type是python当中的元类,python中的任何一个类都是type元类的实例,并且type类也是它自己的一个实例。
一般情况下,我们常用type去进行元素的类型判断,其实,type还可以去创造一个类,也就是说,type的用法有两种:

print(type(1))
type_class = type('type_class',(),{})
#类名+基类+属性
print(type_class)
print(type_class())

结果如下:

<class 'int'>
<class '__main__.type_class'>
<__main__.type_class object at 0x0000000000636518>

通过type创建的类同样可以实例化

2、metaclass的使用

metaclass可以定义一个类的元类是谁,也就是说,我们可以为一个类自定义元类

class A(type):
    pass
class B(A):
    pass
class C(B):
    pass
print(type(C))

结果为:

<class 'type'>

稍微做一下调整:

class A(type):
    pass
class B(metaclass=A):
    pass
class C(B):
    pass
print(type(C))

结果为:

<class '__main__.A'>

可以看出,默认情况下,type是所有类的元类,但是通过metaclass,我们可以改变这个默认。
同时需要注意的一点是,假设一个名为A的类使用了元类参数,那么元类执行的优先级要高于A类,并自动获取A类的类名,属性等。

class Upper(type):
    def __new__(cls,name,base,attr):
        print(attr)
        attr = ((key,value) for key,value in attr.items())
        attr_upper = dict((key.upper(),value) for key,value in attr)
        print(attr_upper)
        return super(Upper,cls).__new__(cls,name,base,attr_upper)
class test(metaclass=Upper):
    pig = '猪猪猪'
t = test()
print(t)
print(t.PIG)

结果为:

{'__module__': '__main__', '__qualname__': 'test', 'pig': '猪猪猪'}
{'__MODULE__': '__main__', '__QUALNAME__': 'test', 'PIG': '猪猪猪'}
<__main__.test object at 0x000000000236F668>
猪猪猪

test继承了Upper类,在调用test类时,直接跳入到元类中,获取test类属性并更改,所以在后面实例化 test的后,通过无法通过pig提取出其属性,因为其名字已经被修改了。

3、通过元类进行ORM创建,实现对数据库的数据插入

代码如下:

import pymysql


class ModelMetaClass(type):  #首先自定义一个元类
    def __new__(cls, name,bases,attrs): #然后通过new方法,创建元类对象
        '''
        :param name: 类的名称
        :param bases: 类的继承
        :param attrs:  类的属性
        :return:
        '''
        # 因为数据插入的缘故,要将类名作为表名,如果获取到的实例类 类名为model,则原封不动返回,否则,进行修改
        if name == "Model":
            return type.__new__(cls,name,bases,attrs)
        mapping = dict()  #创建一个空的dict,用来存储修改后的实例类的属性
        print(attrs) #打印当前属性内容,为字典格式
        print('===================\n')
        print('第一次kv>>>>>')
        print('===================\n')
        for k,v in attrs.items():
            print(k,v)
            if isinstance(v,Field):
                mapping[k] = v #遍历取出,k为实例类的变量名,v则是field子类的实例,也就是变量的值
        print('===================\n')
        for k in mapping.keys():
            attrs.pop(k) #再在attr中删除键值对,防止数据对实例类数据污染
        attrs["__mapping__"] = mapping #这就相当于在实例类中设置了一个类属性:__mapping__ = {k:v,k:v}
        attrs["__table__"] = name #这就相当于设置了一个类名的类属性: __table__ = 当前实例类类名
        print('__mapping__属性>>>>>')
        print(mapping)
        print('__table__属性>>>>>')
        print(name)
        print('===================\n')
        return type.__new__(cls,name,bases,attrs) #将改变返赋值给实例类

class Model(dict,metaclass = ModelMetaClass): #创建一个实例类,其元类为ModelMetaClass

    def __init__(self,**kwargs):
        self.db = pymysql.connect(  #连接数据库
            host = "localhost",
            user = "root",
            password = "123",
            database = "school"
        )
        self.cursor = self.db.cursor()
        super(Model,self).__init__(**kwargs)

    def __getattr__(self, key):  #定义一个获取key值的方法
        return self[key]

    def __setattr__(self, key, value): #定义一个修改键值对的方法
        self[key] = value

    def save(self):  #定义一个存储的方法
        fields = []
        args = []
        print('===================\n')
        print('第二次kv>>>>>')
        print('===================\n')
        for k,v in self.__mapping__.items(): #遍历__mapping__
            print('--------------\n')
            print('第二次k,v的值>>>>>')
            print(k,v) #k也是实例类的变量名,v则是变量的值,与上方的k,v一样
            print('--------------\n')
            fields.append(v.name) #此时,v为field子类的实例,因此可以取出类属性name,作为字段名
            args.append(getattr(self,k,None)) #此时调用了self,则将传入的参数调用,此时再取变量的值,则为传入的实参
            print('v的name属性>>>>>')
            print(v.name)
            print('self后的k键的值>>>>>')
            print(getattr(self,k,None))
        print('===================\n')

        sql = "insert into %s(%s) values (%s)"%(  #拼接sql语句
            self.__table__,
            ",".join(fields),
            ",".join([repr(str(i)) for i in args]
               )) #sql拼接
        self.cursor.execute(sql)
        print(sql)
    def __del__(self):
        '''
        回收内存
        '''
        self.db.commit()
        self.cursor.close()
        self.db.close()

class Field(object):
    def __init__(self,name,column_type):
        self.name = name
        self.column_type = column_type
    def __str__(self):
        return "<%s:%s>"%(self.name,self.column_type)

class StringField(Field):  #StringField('name')的实例实际上就是父类的return
    def __init__(self,name):
        super(StringField,self).__init__(name,"varchar(100)")

class IntegerField(Field):
    def __init__(self,name):
        super(IntegerField,self).__init__(name,"int")

class Student(Model):  #Model实例类的子类
    name = StringField("name")
    room_id = IntegerField("room_id")


u = Student(name = "老边",room_id = 18) #Student是一个字典的对象
print('实例化后的类属性(键值)>>>>>')
print(u.name) #实参传入后的Student的键name的值
print(u.room_id) #实参传入后的Student的键room_id的值

u.save()

输出结果为:

===================

第一次kv>>>>>
===================

__module__ __main__
__qualname__ Student
name <name:varchar(100)>
room_id <room_id:int>
===================

__mapping__属性>>>>>
{'name': <__main__.StringField object at 0x000000000226FA90>, 'room_id': <__main__.IntegerField object at 0x000000000226FBA8>}
__table__属性>>>>>
Student
===================

实例化后的类属性(键值)>>>>>
老边
18
===================

第二次kv>>>>>
===================

--------------

第二次k,v的值>>>>>
name <name:varchar(100)>
--------------

v的name属性>>>>>
name
self后的k键的值>>>>>
老边
--------------

第二次k,v的值>>>>>
room_id <room_id:int>
--------------

v的name属性>>>>>
room_id
self后的k键的值>>>>>
18
===================

insert into Student(name,room_id) values ('老边','18')

执行后,成功向数据库插入了数据,大部分解释都写在了代码当中。
其中,元类的特性以、执行顺序等都是需要中点学习的地方。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值