1. 程式人生 > >metaclass(元類)的小探索

metaclass(元類)的小探索

1.python 的metaclass

關於元類 在python中,有一經典解釋文章,特別是注意到,而python中的基本元類就是type,type會根據傳入引數的個數來決定發揮列印型別的函式功能或者發揮元類的作用。元類的自定義使用__metaclass__:

(一)自定義metaclass

使用metaclass只需宣告:
__metaclass__ = something..
放置元類的位置可以是任意的,python直譯器進行查詢元類的順序這篇文章中已經詳細說明:如果Python沒有找到__metaclass__,它會繼續在Bar(父類)中尋找__metaclass__屬性,並嘗試做和前面同樣的操作。如果Python在任何父類中都找不到__metaclass__,它就會在模組層次中去尋找__metaclass__,並嘗試做同樣的操作。如果還是找不到__metaclass__,Python就會用內建的type來建立這個類物件。”
值得注意的是,放置位置帶來的區別。
放置於:模組級(module level)、類級(class level)、子類或者父類中。。<稍後講到> 那麼作為建立類的類(類工廠),metaclass可以是哪些“東西”呢?
  1. 函式作為metaclass
  2. 類作為metaclass

1).定義函式做為metaclass的情況:

def metafunc(classname,bases,attrs):
	for key,value in attrs.items():
			if isinstance(value,int):
				attrs[key.upper()]=value
				attrs.pop(key)
	print 'In[%s:From-->%s]'%(classname,bases)
	return type(classname,bases,attrs)
函式metafunc是定義好的作為__metaclass__元類屬性的函式,其作用便是將要建立的類中的int型別的屬性名大寫。其實也就是在類建立前“做些事”之後,再利用type建立想要的類。值得注意的是:
#此時如果將元類屬性放置在module level,發現無任何作用,不知道是不是python版本不同導致
__metaclass__ = metafunc
class MyClass(object):   
    lower = 123
    strs = 'string in MyClass..'
class MySubClass(MyClass):
    kid_lower = 456
    kid_strs = 'string in MySubClass..'
#此時若把元類屬性放置於父類MyClass或者子類MySubClass中,發現只有放置了__metaclass__屬性的類會達到預期的效果
class MyClass(object):
    __metaclass__ = metafunc
    lower = 123
    strs = 'string in MyClass..'
class MySubClass(MyClass):
    __metaclass__ = metafunc
    kid_lower = 456
    kid_strs = 'string in MySubClass..'
若將__metaclass__只放置於父類MyClass中,子類並不會被影響...這個不知為何,得同時寫進類裡<環境:python 2.7.11 in win7 64bits> 【輸出:】 放置於父類結果:
In[MyClass:From-->(<type 'object'>,)]
放置於子類的結果:
In[MySubClass:From-->(<class '__main__.MyClass'>,)]

2).定義類做為metaclass的情況:

class testMetaclass(type):
	def __new__(cls,name,bases,attrs):
		for key,value in attrs.items():
			if isinstance(value,int):
				attrs[key.upper()]=value
				attrs.pop(key)
		print 'In[%s:From-->%s]==>testMetaclass'%(name,bases)
		return super(testMetaclass,cls).__new__(cls,name,bases,attrs)
	def __call__(cls,*args):
		print 'call metaclass'
		return super(testMetaclass,cls).__call__(*args)
自定義類testMetaclass(一般定義元類的命名可以在類名末尾加上Metaclass :P)作為__metaclass__元類屬性,在元類建立類的時候,呼叫__new__方法,要在建立前“做些事”也是在這個方法中實現。__call__方法是建立的類例項化的時候才會被呼叫。這個也可以在例項化前“做些事”..:) 值得注意的是:
#若把__metaclass__屬性放置於module level,也是任何作用都沒有。。囧。但是把其只放置於父類Myclass中,子類也會被影響;把其只放置於子類中,父類不會被影響。
class MyClass(object):
    __metaclass__ = testMetaclass
    lower = 123
    strs = 'string in MyClass..'
class MySubClass(MyClass):
    kid_lower = 456
    kid_strs = 'string in MySubClass..'
【輸出:】 放於父類的結果:
In[MyClass:From-->(<type 'object'>,)]==>testMetaclass
In[MySubClass:From-->(<class '__main__.MyClass'>,)]==>testMetaclass
放於子類的結果:
In[MySubClass:From-->(<class '__main__.MyClass'>,)]==>testMetaclass


(二)我用metaclass幹嘛啊?

一般情況下,如果要對方法或者屬性進行使用前的“額外操作”,可以使用decorator 或者 descriptor ,對於動態的建立類也是有很多方法可以實現,不過,metaclass可以讓我們在想要對類建立前進行各種操作需求的時候方便的提供工作。
  • 攔截類的建立,對傳入的要建立的類的各項引數進行操作;<如果在module level可以進行類的攔截,那....>
  • 篩選要建立的類,進行鍼對性的操作。
  • 檢查介面API
  • 等等等

(三)一些相關連結