您好,登錄后才能下訂單哦!
這篇文章主要介紹python源碼剖析之PyObject的示例分析,文中介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們一定要看完!
Python中一切皆是對象。
————Guido van Rossum(1989)
這句話只要你學過python,你就很有可能在你的Python學習之旅的前30分鐘就已經見過了,但是這句話具體是什么意思呢?
一句話來說,就是面向對象中的“類”和“對象”在Python中都是對象。類似于int對象的類型對象,實現了“類的概念”,對類型對象“實例化”得到的實例對象實現了“對象”這個概念。
通常的說法是,對象是數據以及基于這些數據的操作的集合。在計算機上,一個對象實際上就是一片被分配的內存空間,這些內存可能是連續的,也有可能是離散的,這都不重要,重要的是這片內存在更高的層次上可以作為一個整體來考慮,這個整體就是一個對象。在這片內存中,存儲著一系列的數據以及可以對這些數據進行修改或讀取的一系列操作的代碼。
在 Python 中,對象就是在堆上申請的結構體,對象不能是被靜態初始化的,并且也不能是在棧空間上生存的。唯一的例外就是類型對象(type object),Python中所有的類型對象都是被靜態初始化的。在 Python 中,一個對象一旦被創建,它在內存中的大小就是不變的了。 這就意味著那些需要容納可變長度數據的對象只能在對象內維護一個指向一個可變大小的內存區域的指針。
PyObject
和 PyVarObject
分別表示定長對象和變長對象,使用的C的struct
實現的,在結構中分別只定義了 PyObject_HEAD
和 PyObject_VAR_HEAD
,后者僅僅是前者加上一個表示容納元素個數的ob_size
。
[object.h] /* PyObject_HEAD defines the initial segment of every PyObject. */ #define PyObject_HEAD \ _PyObject_HEAD_EXTRA \ int ob_refcnt; \ struct _typeobject *ob_type; #define PyObject_VAR_HEAD \ PyObject_HEAD \ int ob_size; /* Number of items in variable part */
而對于兩者共有的PyObject_HEAD
中,只有兩個東西,一個是維護引用計數的ob_refcnt
和一個指向類型對象PyTypeObject
結構體的指針。因此, Python 中實際上對象機制的核心非常的簡單,一個是引用計數,一個就是類型。并且Python中每一個對象的開始字節都是相同的頭部,這使得對Python對象的引用十分統一,只需要一個PyObject*
可以引用任意一個對象。
這兩個結構體定義的只是Python中對象共有的部分,其他的具體類型會有額外的結構體來定義,否則的話所有的對象豈不是都一樣了?比如int類型的結構體定義PyIntObject
中包含了PyObject_HEAD
和ob_ival
后者是一個long,存放具體的值。
那初始化對象的時候,去那里獲得對象的大小呢?只能是在類型對象PyTypeObject
中了!類型對象中存放了大量對象的元信息,大小顯然是一種和對象的類型有關的元信息!注意,一個PyObject對象就是Python中對面向對象理論中類這個概念的實現,這里面存放了類型名、內存空間、操作函數指針等信息。
Python會用兩種方法創建對象,一種是泛型API(AOL:Abstract Object Layer),可以應用在任何Python對象上,API內不會有機制確定最終調用哪個具體函數,比如PyObject_New(PyObject, &PyInt_Type)
。另一種是類型相關API(COL:Concrete Object Layer),只能應用于具體類型的對象上,比如PyInt_FromLong(10)
。
自定義對象在Python內部不可能存在COL,所以只能根據其類型對象來創建實例對象,這就需要PyTypeObject
中的tp_new
函數指針,如果是自定義對象,這個指針可能是空,那就通過PyTypeObject
的tp_base
找到類型對象的基類,再找tp_new
指針,這個過程中會利用類型對象中記錄的空間信息申請內存。對于 Python 中的任何一種變長對象,tp_itemsize
這個域是必須設置的,tp_itemsize
指明了由變長對象保存的元素的單位長度,所謂單位長度即是指一個對象在內存中的長度。這個 tp_itemsize
和ob_size
共同決定了應該額外申請的內存的總大小是多少。
內建對象最終會使用COL完成創建工作。
new
函數完成后,會執行init
函數,前者類似于new操作符,后者類似于構造函數。
像前面說的,對象的行為被類型對象中的函數指針所定義。這些操作中,有三組非常重要的操作族:tp_as_number
、tp_as_sequence
、tp_as_mapping
分別指向PyNumberMethods
、PtSequenceMethods
、PyMappingMethods
函數族結構體。所謂“鴨子類型”,就行能找到該類型對應的操作,就可以當做這個類型來用。
class MyInt(int): def __getitem__(self, key): return key+str(self) a=MyInt(1) b=MyInt(2) print(a+b) print(a['somekey'])
可以發現通過int繼承得到的數值對象,通過重寫魔術方法,使其支持了字典類型的操作,其實我們可以認為是,制定了MyInt這個類型對象的tp_as_mapping.mp_subscript
操作。
之前說了,作為類的實現的類型對象也是Python對象,那么類型對象PyObject
的ob_type
指針指向哪呢?是指向自己嗎?盡管我一開始也是這么想的,但可惜這個答案不對,類型對象指向的對象是PyType_Type。這個對象在Python類型機制中很重要,所有用戶自定義class的PyTypeObject
對象都是通過這個對象創建的,因此他是Python中的元類(metaclass)。他是所有class的class。而元類的類型是自己,這里出現了我們一開始認為會出現的自己只想自己的情況!
i=1 class A: pass a=A() print(i.__class__) # 類型對象 print(i.__class__.__class__) # 元類 print(a.__class__) # 類型對象 print(a.__class__.__class__) # 元類 print(a.__class__.__class__.__class__) # 指向自己
留在這里的疑問:虛線和虛線指向的對象是啥玩?
通過 PyObject 和類型對象, Python 利用 C 語言完成了 C++所提供的繼承和多態的特性。一開始已經提到,Python中所有對象的前面幾個字節都是PyObject類型也就是PyObject_HEAD
結構體。因此在 Python 內部各個函數之間傳遞的都是一種范型指針PyObject*
。這個指針所指的對象究竟是什么類型的,不知道,只能從指針所指對象的ob_type
域判斷,而正是通過這個域,Python 實現了多態機制。
真正執行的時候,通過找到實例對象指向的類型對象的函數指針來執行方法。這里同一個函數在不同情況下表現出了不同的行為,這正是多態的核心所在。
在 Python 中,主要是通過Py_INCREF(op)
和Py_DECREF(op)
兩個宏來增加和減少一個對象的引用計數。當一個對象的引用計數減少到 0 之后, Py_DECREF
將調用該對象的析構函數(deallocator function)(但是不一定真的釋放該對象所占有的內存和系統資源),即類型對象中tp_dealloc
指向的函數。例外的是類型對象,PyTypeObject
是超越引用計數規則的,永遠不會被析構,每一個對象中指向類型對象的指針不被視為對類型對象的引用。
這有些觀察者模式(Observer)的影子,在ob_refcnt
減為 0 之后,將觸發對象銷毀的事件;從 Python 的對象體系來看,各個對象又提供了不同的事件處理函數,而事件的注冊動作正是在各個對象對應的類型對象中靜態完成的。
在PyObject
中我們看到ob_refcnt
是一個 32 位的整形變量,這實際是一個Python所做的假設,即對一個對象的引用不會超過一個整形變量的最大值。
1、云計算,典型應用OpenStack。2、WEB前端開發,眾多大型網站均為Python開發。3.人工智能應用,基于大數據分析和深度學習而發展出來的人工智能本質上已經無法離開python。4、系統運維工程項目,自動化運維的標配就是python+Django/flask。5、金融理財分析,量化交易,金融分析。6、大數據分析。
以上是“python源碼剖析之PyObject的示例分析”這篇文章的所有內容,感謝各位的閱讀!希望分享的內容對大家有幫助,更多相關知識,歡迎關注億速云行業資訊頻道!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。