Python中类的创建-metaclass

metaclass很少被用到,以至于学习Python时你根本可以先不用管它。最近跟着廖雪峰大神编写orm,所以来看看metaclass的用法。

metaclass的作用

metaclass是指定类由谁创建的,它能够定制类的创建过程。

object和type是python中的两个源对象,python的新式类中都是继承自object类,而<class ‘object’>的子类的类型都是<class ‘type’>。具体object和type的关系可以见这篇文章Python中的type和object【转载】

因此,当我们创建一个类时:

class Test(object):
	pass

其实,在python解释器中,上述语句被解释成:

Test=type('Test',(object,),{})   

三个参数分别为:类名;类的继承关系;类的字段,方法等。

因此,我们可以使用type来创建一个类。

class Father(object):
pass

class Man(object):
	pass
	
s=type('Child',(Father,Man,),{'speak':'hello, how are you.'})
print(s.speak)
print(s.__dict__)
print(s.__bases__)
print(s.__class__)  

运行结果为:

hello, how are you.
{'speak': 'hello, how are you.', '__module__': '__main__', '__doc__': None}
(<class '__main__.Father'>, <class '__main__.Man'>)
<class 'type'>

如果我们想要自定义类的创建过程,我们可以继承type。

自定义类的创建

# -*- coding:utf-8 -*-

class Field(object):

	def __init__(self,name,column_type):
		self.name=name
		self.column_type=column_type
		
	#当使用print时,python会调用它的str方法
	#两个参数为:类的名字,实例的name参数
	def __str__(self):
		return "<%s:%s>" %(self.__class__.__name__,self.name)
		
		
#定义各种类型的Field
class StringField(Field):
	
	def __init__(self,name):
		#调用父类的init函数
		super(StringField,self).__init__(name,"varchar(100)")
		
class IntegerField(Field):
	
	def __init__(self,name):
		super(IntegerField,self).__init__(name,"bigint")
		

#编写ModelMetaclass
class ModelMetaclass(type):
	
	#__new__方法的参数
	#准备创建的类的对象, cls
	#类的名字,name
	#类继承的父类集合,bases
	#类的属性的集合,attrs
	def __new__(cls,name,bases,attrs):
		#若新建的类名为Model,则不做修改
		if name=='Model':
			return type.__new__(cls,name,bases,attrs)
		print("Found model:%s"% name)
		mappings=dict()
		for k,v in attrs.items():
			if isinstance(v,Field):
				print("Found mappings:%s  :  %s"%(k,v))
				mappings[k]=v 
		#将映射从方法集合attrs中挑出,组成__mappings__
		#而后把__mappings__添加到attrs中
		for k in mappings.keys():
			attrs.pop(k)
		attrs["__mappings__"]=mappings
		attrs["__table__"]=name #添加表名
		return type.__new__(cls,name,bases,attrs)   
		

#编写Model基类
#继承dict
#指定metaclass,能够定制类的创建过程
class Model(dict,metaclass=ModelMetaclass):

	def __init__(self,**kw):
		#调用dict的初始化方法
		super(Model,self).__init__(**kw)
	
	#令可以用d.k获取key的值
	def __getattr__(self,key):
		try:
			return self[key]
		except KeyError:
			raise AttributeError(r"'Model' object has no attribute '%s'"%key)
	
	#可以使用d.k动态设置key的值
	def __setattr__(self,key,value):
		self[key]=value
		
	def save(self):
		fields=[]
		params=[]
		args=[]
		#在所有映射中迭代
		for k, v in self.__mappings__.items():
			fields.append(v.name)
			params.append("?")
			args.append(getattr(self,k,None))
		sql="insert into %s (%s) values (%s)" %(self.__table__,",".join(fields),",".join(params))
		print("SQL:%s" %sql)
		print("ARGS: %s" % str(args))
		
		
#定义User类,对应了数据库中的表User
class User(Model):
	#定义类的属性到列的映射
	id=IntegerField("id")
	name=StringField("username")
	password=StringField("password")
	
u=User(id=123,name="dxk",password="123456")
u.save()
print(u.__mappings__)
print(u.__table__)

运行结果为:

Found model:User
Found mappings:id  :  <IntegerField:id>
Found mappings:name  :  <StringField:username>
Found mappings:password  :  <StringField:password>
SQL:insert into User (id,username,password) values (?,?,?)
ARGS: [123, 'dxk', '123456']
{'id': <__main__.IntegerField object at 0x00000013DB505390>, 'name': <__main__.S
tringField object at 0x00000013DB5053C8>, 'password': <__main__.StringField obje
ct at 0x00000013DB505400>}
User

有关__new__的简单介绍可以看:Python中__new__和__init__的简单介绍

上述代码的执行流程:

解释器执行至class Model时,得知其由ModelMetaclass创建。解释完类内的方法后,进入ModelMetaclass解释__new__方法,由于没有__init__,执行type中的__init__。此时__new__中的cls为Model。

解释器执行到class User后,解释完定义的字段后,执行ModelMetaclass的__new__方法。此时cls为User,name为‘User’,base为Model,attrs为包含字段的字典。

可以看到,在ModelMetaclass中,我们对相应类中的属性attrs进行操作,比如User类中的id,name等。我们使用元类就是修改一些特性,构造自己需要的属性并且返回类。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值