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