類與元類(metaclass)的理解

類與元類(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)   返回修改之後的類
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章