先來了解下python魔法的內核吧:
一切皆對象
- 一切皆對象
- 一切皆有類型
- “class” and "type" 之間本質上上并無不同
- 類也是對象
- 他們的類型都是type
class ObjectCreator(object):
pass
這段代碼將在內存中創建一個對象,名字叫ObjectCreator.
這個對象(類)自身擁有創建對象的(類實例的能力),##
而這也就是它為什么是一個類的原因##
你可以對該對象進行以下操作:
- 將它賦值給一個變量
- 可以拷貝它
- 可以為它增加屬性
- 可以將其作為函數參數進行傳遞
#交互型
>>> def echo(o):
... print(o)
...
>>> echo(my_object)
<__main__.ObjectCreator object at 0x7fcf7d0d6c50> #你可以將類做為參數傳給函數
>>> ObjectCreator.new_attrituer=echo # 你可以為類增加屬性
>>> print(hasattr(ObjectCreator,'new_attrituer'))
True
>>> print(ObjectCreator.new_attrituer) #新加的類的方法屬性
<function echo at 0x7fcf7d171f28>
一:用type創建一個類
type(類名, 父類的元組(針對繼承的情況,可以為空),包含屬性的字典(名稱和值))
#三個參數的類型分別是str,tuple,dic
比如:
#目標類:
class Animal(object):
def __init__(self,name):
self.name=name
def eat(self):
pass
def go_to_eat(self):
pass
#用type創建
def init(self,name):
self.name=name
def eat(self):
pass
def go_to_eat(self):
pass
Animal=type('Animal',(object,),{
'__init__':init,
'eat':eat,
'go_to_eat':go_to_eat
})
>>>print(Animal)
<class '__main__.Animal'>
這樣我們實現了用type去動態創建一個類的
BUT~這樣是不是太麻煩了!!!(抬走下一位!)
二:metaclass登場
metaclass,直譯為元類,簡單的解釋就是:
當我們定義了類以后,就可以根據這個類創建出實例,所以:先定義類,然后創建實例。
但是如果我們想創建出類呢?那就必須根據metaclass創建出類,所以:先定義metaclass,然后創建類。
連接起來就是:先定義metaclass,就可以創建類,最后創建實例。
所以,metaclass允許你創建類或者修改類。換句話說,你可以把類看成是metaclass創建出來的“實例”。
# metaclass是創建類,所以必須從`type`類型派生:
class Listmetaclass(type):
def __new__(cls,name,bases,attrs):
attrs['add']=lambda self,value:self.append(value)
return type.__new__(cls,name,bases,attrs)
class Mylist(list):
__metaclass__=Listmetaclass
- 按照默認習慣.metaclass的類名總是以Metaclass結尾,以便清楚地表示這是一個metaclass
- 當我們寫下__metaclass__=Listmetaclass時候,它表示解釋器在創建Mylist的時候
,要通過Listmetaclass的__new__()方法創建
在此,我們可以修改類的定義,比如:加上新的方法,然后返回修改后的定義. - __new__()方法接受的參數依次是:
- 當前準備創建的類的對象
- 類的名字
- 類繼承的父類的集合
- 類的方法的集合
元類的主要目的就是為了當創建類時能夠自動改變類.
使用到元類的代碼比較復雜,這背后的原因倒并不是因為元類本身,
而是因為你通常會使用元類去做一些晦澀的事情,
依賴于自省,控制繼承等等。
確實,用元類來搞些“黑暗魔法”是特別有用的,
因而會搞出些復雜的東西來。
就這個例子來說:
- 攔截類的創建(攔截Mylist的創建)
- 修改類(給Mylist增加add的方法)
- 返回修改之后的類
**“元類就是深度的魔法,99%的用戶應該根本不必為此操心。如果你想搞清楚究竟是否需要用到元類,那么你就不需要它。那些實際用到元類的人都非常清楚地知道他們需要做什么,而且根本不需要解釋為什么要用元類。” **-----Tim Peters
元類的主要作用的創建API,一個典型的例子是 ORM.
ORM全稱“Object Relational Mapping”,
即對象-關系映射,就是把關系數據庫的一行映射為一個對象,
也就是一個類對應一個表,
這樣,寫代碼更簡單,不用直接操作SQL語句。
~
要編寫一個ORM框架,所有的類都只能動態定義,
因為只有使用者才能根據表的結構定義出對應的類來。
我們來嘗試寫一個簡單的ORM框架吧
首先我們要知道使用者會調用什么接口:
比如,使用者可能會定義一個User類來操作數據表User.
class User(Model):
#創建User表
#創建四列屬性
id=IntegerField('id')
name=StringField('username')
email=StringField('email')
password=StringField('password')
# 創建一個實例:
u=User(id='12345',name='zhou',email='124@qq.com',password='123455')
#保存到數據庫
u.save()
首先,我們來定義Field類,它負責保存數據庫表的字段名字跟字段類型:
class Field(object):
def __init__(self,name,column_type):
self.name=name
self.column_type=column_type
def __str__(self):
return '<%s:%s>' %(self.__class__.__name__,self.name)
在Field的基礎上,我們來定義各種Field:
#為了簡化,我們先定義好各種屬性的type
class StringField(Field):
def __init__(self,name):
super(StringField,self).__init__(name,'varchar(100)')
class IntegerField(Field):
def __init__(self,name):
super(IntegerField,self).__init__(name,'bigint')
接下來,我們要編寫元類了
class ModelMetaclass(type):
def __new__(cls,name,bases,attrs):
#如果是基類,直接返回(即不對model類進行修改)
if name=='Model':
return type.__new__(cls,name,bases,attrs)
print('Found model: %s' %name)
#如果是其他類,則進行裝飾
#取出所有類屬性,將其放入mapping
mappings=dict()
for key,value in attrs.iteritems():
if isinstance(value,Field):
print('Found mapping: %s==>%s' %(key,value))
mappings[key]=value
for key in mappings.iterkeys():
attrs.pop(key)
attrs['__mappings__']=mappings
attrs['__table__']=name
return type.__new__(cls,name,bases,attrs)
編寫基類:
class Model(dict):
__metaclass__=ModelMetaclass
def __init__(self,**kw):
super(Model,self).__init__(**kw)
def __getattr__(self,key):
try:
return self[key]
except KeyError:
raise AttributeError(r"'Model' object has no Attribute '%s'" %key)
def __setattr__(self,key,value):
self[key]=value
def save(self):
fields=[]
params=[]
args=[]
for key,value in self.__mappings__.iteritems():
fields.append(value.name)
params.append('?')
args.append(getattr(self,key,None))
sql='insert into %s(%s) values(%s)' %(self.__table__,','.join(fields),','.join(params))
print('SQL:%s' %sql)
print('ARGS:%s' %str(args))
當用戶自定義一個class User(Model)時候,
解釋器首先在當前類User的定義中尋找
metaclass,如果沒找到,就在繼承的父類中繼續找.,
找到了,就用model中的定義的ModelMetaclass去創建User類
也就是說,metaclass可以隱式地繼承到子類,但子類自己卻感覺不到。
在ModelMetaclass中做的事情:
- 排除對Model 的修改
- 在當前類 中查找定義的類的屬性,如果找到一個FIeld屬性,就保存到__mappings__字典中
同時從類屬性中刪除Field屬性,否則容易造成運行時的錯誤 - 把表名保存到__table__中
在Model 類中,就可以定義各種操作數據庫的方法了(列屬性保存在__mappings__)中
全部代碼:
#!/usr/bin/python3
class Field(object):
def __init__(self,name,column_type):
self.name=name
self.column_type=column_type
def __str__(self):
return '<%s:%s>' %(self.__class__.__name__,self.name)
class StringField(Field):
def __init__(self,name):
super(StringField,self).__init__(name,'varchar(100)')
class IntegerField(Field):
def __init__(self,name):
super(IntegerField,self).__init__(name,'bigint')
class ModelMetaclass(type):
def __new__(cls,name,bases,attrs):
if name=='Model':
return type.__new__(cls,name,bases,attrs)
print('Found model: %s' %name)
mappings=dict()
for key,value in attrs.iteritems():
if isinstance(value,Field):
print('Found mapping: %s==>%s' %(key,value))
mappings[key]=value
for key in mappings.iterkeys():
attrs.pop(key)
attrs['__mappings__']=mappings
attrs['__table__']=name
return type.__new__(cls,name,bases,attrs)
class Model(dict):
__metaclass__=ModelMetaclass
def __init__(self,**kw):
super(Model,self).__init__(**kw)
def __getattr__(self,key):
try:
return self[key]
except KeyError:
raise AttributeError(r"'Model' object has no Attribute '%s'" %key)
def __setattr__(self,key,value):
self[key]=value
def save(self):
fields=[]
params=[]
args=[]
for key,value in self.__mappings__.iteritems():
fields.append(value.name)
params.append('?')
args.append(getattr(self,key,None))
sql='insert into %s(%s) values(%s)' %(self.__table__,','.join(fields),','.join(params))
print('SQL:%s' %sql)
print('ARGS:%s' %str(args))
class User(Model):
id=IntegerField('id')
name=StringField('username')
email=StringField('email')
password=StringField('password')
u=User(id='12345',name='zhou',email='124@qq.com',password='123455')
u.save()
運行結果:
Found model: User
Found mapping: email==><StringField:email>
Found mapping: password==><StringField:password>
Found mapping: id==><IntegerField:id>
Found mapping: name==><StringField:username>
SQL:insert into User(password,email,username,id) values(?,?,?,?)
ARGS:['123455', '124@qq.com', 'zhou', '12345']
最后說幾句:
-
元類(Metaclass)類似于裝飾器,可以在類的創建時動態修改類.
-
元類語法糖:__metaclass__
學習參考:
廖雪峰Python教程
深入理解元類
5分鐘理解元類