類與元類(metaclass)的理解
新博客地址:http://bendawang.site/article/python%E6%B7%B1%E5%85%A5%E5%AD%A6%E4%B9%A0-%E4%B8%80-%EF%BC%9A%E7%B1%BB%E4%B8%8E%E5%85%83%E7%B1%BB%EF%BC%88metaclass%EF%BC%89%E7%9A%84%E7%90%86%E8%A7%A3(ps:短期內csdn和新博客會同步更新)
0x00 前言
最近準備開始着手畢設的東西,所以打算把過程中一些關於python的一些我平時不怎麼使用的地方學習並寫出來分享下,不過由於有些地方是我個人的理解,所以多少可能會有偏差,希望師傅們看到能幫我指出來。
昨天在看代碼的時候遇到了元類(metaclass
),相信平時如果大家用python方面的工具用的較多的話也會比較常見到這個,但即便如此卻沒有去深入研究過這個東西
先分享一個鏈接:http://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python
0x01 關於類、對象、類的實例
首先先談談python中的類、對象和實例。
在C++中接觸到這些東西,不過我的大部分概念僅僅停留在class
關鍵字上面。也經常說道這三個名詞,可能平時用着用着也沒有深究過,漸漸也就用混淆了這三個詞。
1.1 python中靜態與動態創建類
在這裏,一直以來我的理解是類就是通常意義上的class
,而對象就是類的實例化,換言之,類就是一組用來描述如何生成一個對象的代碼段。但是實際上在python裏面實例和對象有區別,待會我們會說,先來看個例子。
例如如下:
class test():
pass
mine=test() #test是類,mine是實例也是對象
但是,Python中的類還遠不止如此。類同樣也是一種對象。因爲只要你使用關鍵字class
,Python解釋器在執行的時候就會創建一個對象。
即test本身也是一個對象,可以進行任何對象的操作,例如賦值、傳參、增加屬性等等。
>>> class test():
... pass
...
>>> print test
__main__.test
>>> test.a='a'
>>> hasattr(test,'a')
>>> print test()
<__main__.test instance at 0x7f8e5caccea8>
>>> print test().a
a
另外在python中除了用關鍵字class
靜態創建類以外還可以用type()
函數動態創建類,這也是廣爲人知的type
函數不廣爲人知的用法,具體用法如下:
type(類名, 父類的元組(針對繼承的情況,可以爲空),包含屬性的字典(名稱和值))
---------------------------------------------------------
For example1:
>>> Hello = type('Hello', (object,), dict(hello="hello world!"))
>>> h=Hello()
>>> h.hello
'hello world!'
---------------------------------------------------------
For example2:
>>> def func(self, name='world'):
... print('Hello, %s.' % name)
...
>>> Hello = type('Hello', (object,), dict(hello=func))
>>> h = Hello()
>>> h.hello()
Hello, world.
---------------------------------------------------------
通過type的用法我們知道類也是可以動態創建的。
1.2 python裏面的類和實例
我們分爲python2和python3來說,
在python2中,我們通過下面這個例子來說明
class A: #舊式類
pass
class B(object):#新式類
pass
print B.__class__
print A.__class__
得到這個結果
<type 'type'>
Traceback (most recent call last):
File "class.py", line 9, in <module>
print A.__class__
AttributeError: class A has no attribute '__class__'
第一個print打印了,但是第二個報錯了,我們知道__class__
會告訴我們當前實例是哪個類的實例。這也就意味着,B是一個類也是實例,但A只是一個類,不是實例,這也就是舊式類和新式類的區別。
再看
#encoding:utf-8
class A:
pass
class B(object):
pass
b = B()
a = A()
print "---------------------"
print A
print B
print "---------------------"
print a
print b
print "---------------------"
print type(A)
print type(B)
print "---------------------"
print type(a)
print type(b)
print "---------------------"
結果
---------------------
__main__.A
<class '__main__.B'>
---------------------
<__main__.A instance at 0x7ff0f37087e8>
<__main__.B object at 0x7ff0f3713290>
---------------------
<type 'classobj'>
<type 'type'>
---------------------
<type 'instance'>
<class '__main__.B'>
---------------------
多少能夠理解了把。尤其是第二組打印結果,a是A的一個實例,b是B的一個對象。
即總結起來這麼說,自建類實例化生成的對象的類型是object,內置類實例化生成的對象的類型是instance。
尤其是第三組打印結果,也說明自建類和內置類的區別,自建類的類型是classobj
,而內置類的類型是type
,這一點在後續會解釋的。
大家可能會問,既然自建對象和內置對象不同,但是我們平時使用的時候都是一樣的使用啊?
這個問題就是python官方打算解決的,因爲他底層會用大量代碼來掩蓋二者的差異,從而讓我們覺得在使用中二者一樣。
所以在python3裏面所有類都是內置類了。如下:
class A:
pass
class B(object):
pass
即上述代碼都默認自己懂繼承自object類了。從而幾乎取締了實例instance
的存在。在python3
中測試如下:
>>> class A:
... pass
...
>>> class B(object):
... pass
...
>>> a=A()
>>> b=B()
>>> print(a)
<__main__.A object at 0x7fbb06f81198>
>>> print(b)
<__main__.B object at 0x7fbb0526add8>
可以看到已經沒有區別了。
所以看得出官方的態度就是支持大家更多的使用內置類,而非自建類,即平時我們寫代碼的時候還是儘量多使用B
的創建方式,繼承OBJECT
。
好的,接下來還有一個問題就是我們執行print type(B)
的時候打印的值爲什麼是<type 'type'>
進入下一個主題。
0x02 關於元類metaclass
2.1 元類是什麼?
我們之前說過在python裏面的類本身也是一個對象,元類就是創建所有python類的類
即可以通過這個
Anyclass=Metaclass()
Object=Anyclass()
所以大家應該猜到了把,實際上type
就是這個元類,即python所有的類都是由type
創建的,這也是爲什麼type可以用來動態創建類的原因。
換言之,元類type就是創建python類這種對象的東西,即可以稱爲一個類工廠
,當然,我們也可以創建自己的元類。
2.2 __metaclass__
屬性
這個就是用的比較多的地方。
你可以在寫一個類的時候爲其添加__metaclass__
屬性
class Foo(object):
__metaclass__ = something
......
......
你首先寫下class Foo(object)
,但是類對象Foo還沒有在內存中創建。Python會在類的定義中尋找__metaclass__
屬性,如果找到了,Python就會用它來創建類Foo,如果沒有找到,就會用內建的type
來創建這個類。
即在python創建類的時候,python會在內存中通過__metaclass__
創建一個名字爲Foo的類對象。如果Python沒有找到__metaclass__
,它會繼續在父類中尋找__metaclass__
屬性,並嘗試做和前面同樣的操作。如果Python在任何父類中都找不到__metaclass__
,它就會在模塊層次中去尋找__metaclass__
,並嘗試做同樣的操作。如果還是找不到__metaclass__
,ython就會用內置的type來創建這個類對象。
這就是__metaclass__
屬性,理解這個一定要區分好與繼承的關係。繼承是在類已經在內存創建好了之後繼承相應的屬性和方法,而這個屬性的功能比繼承更強更大,但是簡而言之它的功能其實也很簡單
就是
1) 攔截類的創建
2) 修改類
3) 返回修改之後的類