日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不

當前位置:首頁 > 科技  > 軟件

當創建一個 Python 對象時,背后都經歷了哪些過程?

來源: 責編: 時間:2024-05-21 17:30:25 194觀看
導讀楔子本篇文章來聊一聊對象的創建,一個對象是如何從無到有產生的呢?>>> n = 123>>> n123比如在終端中執行 n = 123,一個整數對象就被創建好了,但它的背后都發生了什么呢?帶著這些疑問,開始今天的內容。Python 為什么這么慢前

楔子

3SN28資訊網——每日最新資訊28at.com

3SN28資訊網——每日最新資訊28at.com

本篇文章來聊一聊對象的創建,一個對象是如何從無到有產生的呢?3SN28資訊網——每日最新資訊28at.com

>>> n = 123>>> n123

比如在終端中執行 n = 123,一個整數對象就被創建好了,但它的背后都發生了什么呢?帶著這些疑問,開始今天的內容。3SN28資訊網——每日最新資訊28at.com

3SN28資訊網——每日最新資訊28at.com

Python 為什么這么慢

3SN28資訊網——每日最新資訊28at.com

3SN28資訊網——每日最新資訊28at.com

前面我們介紹了 Python 對象在底層的數據結構,知道了 Python 底層是通過 PyObject 實現了對象的多態。所以我們先來分析一下 Python 為什么慢?3SN28資訊網——每日最新資訊28at.com

在 Python 中創建一個對象,會分配內存并進行初始化,然后用一個 PyObject * 指針來維護這個對象,當然所有對象都是如此。因為指針是可以相互轉化的,所以變量在保存一個對象的指針時,會將指針轉成 PyObject * 之后再交給變量保存。3SN28資訊網——每日最新資訊28at.com

因此在 Python 中,變量的傳遞(包括函數的參數傳遞)實際上傳遞的都是泛型指針 PyObject *。這個指針具體指向什么類型的對象我們并不知道,只能通過其內部的 ob_type 字段進行動態判斷,而正是因為這個 ob_type,Python 實現了多態機制。3SN28資訊網——每日最新資訊28at.com

比如 a.pop(),我們不知道 a 指向的對象到底是什么類型,它可能是列表、也可能是字典,或者是我們實現了 pop 方法的自定義類的實例對象。至于它到底是什么類型,只能通過 ob_type 動態判斷。3SN28資訊網——每日最新資訊28at.com

如果 a 的 ob_type 為 &PyList_Type,那么 a 指向的對象就是列表,于是會調用 list 類型中定義的 pop 操作。如果 a 的 ob_type 為 &PyDict_Type,那么 a 指向的對象就是字典,于是會調用 dict 類型中定義的 pop 操作。所以變量 a 在不同的情況下,會表現出不同的行為,這正是 Python 多態的核心所在。3SN28資訊網——每日最新資訊28at.com

再比如列表,它內部的元素也都是 PyObject *,因為類型要保持一致,所以對象的指針不能直接存(因為類型不同),而是需要統一轉成泛型指針 PyObject * 之后才可以存儲。當我們通過索引獲取到該指針進行操作的時候,也會先通過 ob_type 判斷它的類型,看它是否支持指定的操作。所以操作容器內的某個元素,和操作一個變量并無本質上的區別,它們都是 PyObject *。3SN28資訊網——每日最新資訊28at.com

從這里我們也能看出來 Python 為什么慢了,因為有相當一部分時間浪費在類型和屬性的查找上面。3SN28資訊網——每日最新資訊28at.com

以變量 a + b 為例,這個 a 和 b 指向的對象可以是整數、浮點數、字符串、列表、元組、甚至是我們自己實現了 __add__ 方法的類的實例對象。因為 Python 的變量都是 PyObject *,所以它可以指向任意的對象,因此 Python 就無法做基于類型的優化。3SN28資訊網——每日最新資訊28at.com

3SN28資訊網——每日最新資訊28at.com

首先 Python 底層要通過 ob_type 判斷變量指向的對象到底是什么類型,這在 C 的層面至少需要一次屬性查找。然后 Python 將每一個算術操作都抽象成了一個魔法方法,所以實例相加時要在類型對象中找到該方法對應的函數指針,這又是一次屬性查找。找到了之后將 a、b 作為參數傳遞進去,這會產生一次函數調用,會將對象維護的值拿出來進行運算,然后根據相加的結果創建一個新的對象,再將對象的指針轉成 PyObject * 之后返回。3SN28資訊網——每日最新資訊28at.com

所以一個簡單的加法運算,Python 內部居然做了這么多的工作,要是再放到循環里面,那么上面的步驟要重復 N 次。而對于 C 來講,由于已經規定好了類型,所以 a + b 在編譯之后就是一條簡單的機器指令,因此兩者在效率上差別很大。3SN28資訊網——每日最新資訊28at.com

3SN28資訊網——每日最新資訊28at.com

當然我們不是來吐槽 Python 效率的問題,因為任何語言都有擅長的一面和不擅長的一面,這里只是通過回顧前面的知識來解釋為什么 Python 效率低。因此當別人問你 Python 為什么效率低的時候,希望你能從這個角度來回答它,主要就兩點:3SN28資訊網——每日最新資訊28at.com

3SN28資訊網——每日最新資訊28at.com

  • Python 無法基于類型做優化;
  • Python 對象基本都存儲在堆上;

建議不要一上來就談 GIL,那是在多線程情況下才需要考慮的問題。而且我相信大部分覺得 Python 慢的人,都不是因為 Python 無法利用多核才覺得慢的。3SN28資訊網——每日最新資訊28at.com

3SN28資訊網——每日最新資訊28at.com

Python 的 C API

3SN28資訊網——每日最新資訊28at.com

3SN28資訊網——每日最新資訊28at.com

然后來說一說 Python 的 C API,這個非常關鍵。首先 Python 解釋器聽起來很高大上,但按照陳儒老師的說法,它不過就是用 C 語言寫出的一個開源軟件,從形式上和其它軟件并沒有本質上的不同。3SN28資訊網——每日最新資訊28at.com

比如你在 Windows 系統中打開 Python 的安裝目錄,會發現里面有一個二進制文件 python.exe 和一個動態庫文件 python312.dll。二進制文件負責執行,動態庫文件則包含了相應的依賴,當然編譯的時候也可以把動態庫里的內容統一打包到二進制文件中,不過大部分軟件在開發時都會選擇前者。3SN28資訊網——每日最新資訊28at.com

既然解釋器是用 C 寫的,那么在執行時肯定會將 Python 代碼翻譯成 C 代碼,這是毫無疑問的。比如創建一個列表,底層就會創建一個 PyListObject 實例,比如調用某個內置函數,底層會調用對應的 C 函數。3SN28資訊網——每日最新資訊28at.com

所以如果你想搞懂 Python 代碼的執行邏輯或者編寫 Python 擴展,那么就必須要清楚解釋器提供的 API 函數。而按照通用性來劃分的話,這些 API 可以分為兩種。3SN28資訊網——每日最新資訊28at.com

  • 泛型 API;
  • 特定類型 API;

泛型 API

顧名思義,泛型 API 和參數類型無關,屬于抽象對象層。這類 API 的第一個參數是 PyObject *,可以處理任意類型的對象,API 內部會根據對象的類型進行區別處理。3SN28資訊網——每日最新資訊28at.com

而且泛型 API 的名稱也是有規律的,具有 PyObject_### 這種形式,我們舉例說明。3SN28資訊網——每日最新資訊28at.com

3SN28資訊網——每日最新資訊28at.com

3SN28資訊網——每日最新資訊28at.com

對象是如何創建的

3SN28資訊網——每日最新資訊28at.com

3SN28資訊網——每日最新資訊28at.com

創建對象可以使用泛型 API,也可以使用特定類型 API,比如創建一個浮點數。3SN28資訊網——每日最新資訊28at.com

使用泛型 API 創建

PyObject* pi = PyObject_New(PyObject, &PyFloat_Type);

通過泛型 API 可以創建任意類型的對象,因為該類 API 和類型無關。那么問題來了,解釋器怎么知道要給對象分配多大的內存呢?3SN28資訊網——每日最新資訊28at.com

在介紹類型對象的時候我們提到,對象的內存大小、支持哪些操作等等,都屬于元信息,而元信息會存在對應的類型對象中。其中 tp_basicsize 和 tp_itemsize 負責指定實例對象所需的內存空間。3SN28資訊網——每日最新資訊28at.com

// Include/objimpl.h#define PyObject_New(type, typeobj)  ((type *)_PyObject_New(typeobj))        // Objects/object.cPyObject *_PyObject_New(PyTypeObject *tp){    // 通過 PyObject_Malloc 為對象申請內存,申請多大呢?    // 會通過 _PyObject_SIZE(tp) 進行計算    PyObject *op = (PyObject *) PyObject_Malloc(_PyObject_SIZE(tp));    if (op == NULL) {        return PyErr_NoMemory();    }    // 設置對象的類型和引用計數    _PyObject_Init(op, tp);    return op;}// Include/cpython/objimpl.hstatic inline size_t _PyObject_SIZE(PyTypeObject *type) {    // 返回類型對象的 tp_basicsize    return _Py_STATIC_CAST(size_t, type->tp_basicsize);}

泛型 API 屬于通用邏輯,而內置類型的實例對象一般會采用特定類型 API 創建。3SN28資訊網——每日最新資訊28at.com

使用特定類型 API 創建

// 創建浮點數,值為 2.71PyObject* e = PyFloat_FromDouble(2.71);// 創建一個可以容納 5 個元素的元組PyObject* tpl = PyTuple_New(5);// 創建一個可以容納 5 個元素的列表// 當然這是初始容量,列表是可以擴容的PyObject* lst = PyList_New(5);

和泛型 API 不同,使用特定類型 API 只能創建指定類型的對象,因為該類 API 是和類型綁定的。比如我們可以用 PyDict_New 創建一個字典,但不可能創建一個集合出來。3SN28資訊網——每日最新資訊28at.com

如果使用特定類型 API,那么可以直接分配內存。因為內置類型的實例對象,它們的定義在底層都是寫死的,解釋器對它們了如指掌,因此可以直接分配內存并初始化。3SN28資訊網——每日最新資訊28at.com

比如通過 e = 2.71 創建一個浮點數,解釋器看到 2.71 就知道要創建 PyFloatObject 結構體實例,那么申請多大內存呢?顯然是 sizeof(PyFloatObject),直接計算一下結構體實例的大小即可。3SN28資訊網——每日最新資訊28at.com

圖片圖片3SN28資訊網——每日最新資訊28at.com

顯然一個 PyFloatObject 實例的大小是 24 字節,所以內存直接就分配了。分配之后將 ob_refcnt 初始化為 1、ob_type 設置為 &PyFloat_Type、ob_fval 設置為 2.71 即可。3SN28資訊網——每日最新資訊28at.com

同理可變對象也是一樣,因為字段都是固定的,內部容納的元素有多少個也可以根據賦的值得到,所以內部的所有字段占用了多少內存可以算出來,因此也是可以直接分配內存的。3SN28資訊網——每日最新資訊28at.com

還是那句話,解釋器對內置的數據結構了如指掌,因為這些結構在底層都是定義好的,源碼直接寫死了。所以解釋器根本不需要借助類型對象去創建實例對象,它只需要在實例對象創建完畢之后,將 ob_type 設置為指定的類型即可(讓實例對象和類型對象建立聯系)。3SN28資訊網——每日最新資訊28at.com

所以采用特定類型 API 創建實例的速度會更快,但這只適用于內置的數據結構,而我們自定義類的實例對象顯然沒有這個待遇。假設通過 class Person: 定義了一個類,那么在實例化的時候,顯然不可能通過 PyPerson_New 去創建,因為底層壓根就沒有這個 API。3SN28資訊網——每日最新資訊28at.com

這種情況下創建 Person 的實例對象就需要 Person 這個類型對象了,因此自定義類的實例對象如何分配內存、如何進行初始化,需要借助對應的類型對象。3SN28資訊網——每日最新資訊28at.com

總的來說,Python 內部創建一個對象有兩種方式:3SN28資訊網——每日最新資訊28at.com

  • 通過特定類型 API,用于內置數據結構,即內置類型的實例對象。
  • 通過調用類型對象去創建(底層會調用泛型 API),多用于自定義類型。

3SN28資訊網——每日最新資訊28at.com

[] 和 list(),應該使用哪種方式

3SN28資訊網——每日最新資訊28at.com

3SN28資訊網——每日最新資訊28at.com

lst = [] 和 lst = list() 都負責創建一個空列表,但這兩種方式有什么區別呢?3SN28資訊網——每日最新資訊28at.com

我們說創建實例對象可以通過解釋器提供的特定類型 API,用于內置類型;也可以通過實例化類型對象去創建,既可用于自定義類型,也可用于內置類型。3SN28資訊網——每日最新資訊28at.com

# 通過特定類型 API 創建>>> lst = [] >>> lst[]# 通過調用類型對象創建>>> lst = list()  >>> lst[]

還是那句話,解釋器對內置數據結構了如指掌,并且做足了優化。3SN28資訊網——每日最新資訊28at.com

  • 看到 123,就知道創建 PyLongObject 實例;
  • 看到 2.71,就知道創建 PyFloatObject 實例;
  • 看到 ( ),就知道創建 PyTupleObject 實例;
  • 看到 [ ],就知道創建 PyListObject 實例;
  • ······

這些都會使用特定類型 API 去創建,直接為結構體申請內存,然后設置引用計數和類型,所以使用 [ ] 創建列表是最快的。3SN28資訊網——每日最新資訊28at.com

但如果使用 list() 創建列表,那么就產生了一個調用,要進行參數解析、類型檢測、創建棧幀、銷毀棧幀等等,所以開銷會大一些。3SN28資訊網——每日最新資訊28at.com

import timestart = time.perf_counter()for _ in range(10000000):    lst = []end = time.perf_counter()print(end - start) """0.2144167000001289"""start = time.perf_counter()for _ in range(10000000):    lst = list()end = time.perf_counter()print(end - start) """0.4079916000000594"""

通過 [ ] 的方式創建一千萬次空列表需要 0.21 秒,但通過 list() 的方式創建一千萬次空列表需要 0.40 秒,主要就在于 list() 是一個調用,而 [ ] 直接會被解析成 PyListObject,因此 [ ] 的速度會更快一些。3SN28資訊網——每日最新資訊28at.com

所以對于內置類型的實例對象而言,使用特定類型 API 創建要更快一些。而且事實上通過類型對象去創建的話,會先調用 tp_new,然后在 tp_new 內部還是調用了特定類型 API。3SN28資訊網——每日最新資訊28at.com

比如:3SN28資訊網——每日最新資訊28at.com

  • 創建列表:可以是 list()、也可以是 [ ];
  • 創建元組:可以是 tuple()、也可以是 ( );
  • 創建字典:可以是 dict()、也可以是 { };

前者是通過類型對象去創建的,后者是通過特定類型 API 創建。但對于內置類型而言,我們推薦使用特定類型 API 創建,會直接解析為對應的 C 一級數據結構,因為這些結構在底層都是已經實現好了的,可以直接用。而無需通過諸如 list() 這種調用類型對象的方式來創建,因為它們內部最終還是使用了 特定類型 API,相當于多繞了一圈。3SN28資訊網——每日最新資訊28at.com

不過以上都是內置類型,而自定義的類型就沒有這個待遇了,它的實例對象只能通過它自己創建。比如 Person 這個類,解釋器不可能事先定義一個 PyPersonObject 然后將 API 提供給我們,所以我們只能通過 Person() 這種調用類型對象的方式來創建它的實例對象。3SN28資訊網——每日最新資訊28at.com

另外內置類型被稱為靜態類,它和它的實例對象在底層已經被定義好了,無法動態修改。我們自定義的類型被稱為動態類,它是在解釋器運行的過程中動態構建的,所以我們可以對其進行動態修改。3SN28資訊網——每日最新資訊28at.com

這里需要再強調一點,Python 的動態性、GIL 等特性,都是解釋器在將字節碼翻譯成 C 代碼時動態賦予的,而內置類型在編譯之后已經是指向 C 一級的數據結構,因此也就喪失了相應的動態性。不過與之對應的就是效率上的提升,因為運行效率和動態性本身就是魚與熊掌的關系。3SN28資訊網——每日最新資訊28at.com

本文鏈接:http://m.www897cc.com/showinfo-26-89706-0.html當創建一個 Python 對象時,背后都經歷了哪些過程?

聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com

上一篇: 高級程序員必須要會的五種編程范式

下一篇: Python 代碼重構的十個關鍵策略

標簽:
  • 熱門焦點
Top 日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不
国产一区高清视频| 欧美日韩成人综合在线一区二区| 亚洲免费在线视频| 午夜在线电影亚洲一区| 久久www成人_看片免费不卡| 久久一区中文字幕| 欧美久久影院| 国产精品视频福利| 伊人精品在线| 99国产精品| 亚洲一区视频在线| 久久精品99国产精品日本 | 欧美一区二区观看视频| 久久久久久国产精品mv| 欧美激情精品久久久久久| 欧美日韩高清在线| 国产日韩精品在线| 91久久亚洲| 亚洲欧美国产精品桃花| 狼人天天伊人久久| 国产精品国产三级国产a| 激情综合网激情| 一区二区不卡在线视频 午夜欧美不卡在| 亚洲伊人观看| 另类av一区二区| 国产精品久久久久77777| 1000精品久久久久久久久| 亚洲午夜精品一区二区| 欧美 日韩 国产精品免费观看| 欧美四级电影网站| 在线观看日韩av电影| 亚洲视频图片小说| 免费日韩视频| 国产三级欧美三级| 日韩亚洲欧美成人| 久久久天天操| 国产精品二区在线| 亚洲国产美国国产综合一区二区| 亚洲小视频在线观看| 久久久久亚洲综合| 国产精品海角社区在线观看| **性色生活片久久毛片| 欧美一级日韩一级| 欧美日韩视频在线观看一区二区三区| 狠狠色伊人亚洲综合网站色| 亚洲专区一二三| 欧美日韩国产91| 影音先锋亚洲视频| 欧美制服丝袜| 国产精品国产三级国产专播品爱网 | 久久午夜视频| 国产欧美日韩一区二区三区| 99在线视频精品| 久久综合伊人77777尤物| 国产精品影音先锋| 亚洲视频综合| 欧美日韩高清一区| 亚洲精选国产| 欧美xart系列高清| 影院欧美亚洲| 久久精品人人爽| 国产区日韩欧美| 亚洲欧美在线免费| 国产精品三上| 亚洲永久精品大片| 欧美视频免费在线| 一本色道久久99精品综合| 欧美激情一区二区三区蜜桃视频 | 久久精品一区二区三区四区| 国产欧美va欧美不卡在线| 亚洲女人天堂成人av在线| 欧美午夜不卡影院在线观看完整版免费 | 国产欧美va欧美不卡在线| 亚洲无线视频| 欧美视频在线视频| 一本一本久久a久久精品综合麻豆| 欧美激情导航| 亚洲精品午夜精品| 欧美成人免费全部观看天天性色| 亚洲第一网站| 女女同性精品视频| 91久久国产综合久久| 欧美成人精品在线| 亚洲日本精品国产第一区| 欧美精品亚洲精品| 一本色道久久综合狠狠躁篇的优点| 欧美人体xx| 中文一区在线| 国产精品一区二区三区观看| 欧美有码视频| 狠狠色伊人亚洲综合成人| 久久综合伊人77777蜜臀| 亚洲国产精品一区二区www在线| 噜噜噜久久亚洲精品国产品小说| 亚洲第一黄色网| 欧美激情第1页| 一区二区不卡在线视频 午夜欧美不卡在 | 一区二区高清| 国产精品久久久久9999吃药| 午夜在线a亚洲v天堂网2018| 国内视频精品| 男人的天堂成人在线| 亚洲乱码一区二区| 国产精品二区在线| 久久精品国产99国产精品| 在线观看国产精品淫| 欧美激情成人在线| 亚洲一区二区视频在线| 国产欧美欧美| 葵司免费一区二区三区四区五区| 亚洲激情在线| 欧美性大战久久久久久久蜜臀| 午夜精品福利在线观看| 影音先锋成人资源站| 欧美精品电影| 亚洲欧美日韩成人| 狠狠色综合日日| 欧美国产一区二区在线观看| 亚洲网站在线| 黄色成人在线观看| 欧美激情在线狂野欧美精品| 亚洲调教视频在线观看| 国产日韩欧美视频在线| 欧美成人资源网| 亚洲男女毛片无遮挡| 精品999在线观看| 欧美日韩国产美| 欧美一区二区视频观看视频| 亚洲国产美女久久久久| 国产精品多人| 老色鬼久久亚洲一区二区| 正在播放亚洲一区| 激情欧美一区二区| 欧美日韩亚洲一区三区| 欧美伊人久久久久久久久影院 | 久久精品国产亚洲a| 日韩亚洲欧美成人一区| 国产欧美一区二区在线观看| 欧美成年人网站| 亚洲欧美国产高清| 亚洲国产你懂的| 国产精品美女久久久| 欧美成人国产| 欧美一区二区三区在线| 日韩视频一区二区三区| 国产主播一区| 国产精品扒开腿做爽爽爽软件| 久久人人爽国产| 亚洲性图久久| 亚洲欧洲在线一区| 国产一区二区成人| 欧美视频在线免费看| 蜜桃精品久久久久久久免费影院| 亚洲性人人天天夜夜摸| 亚洲国产激情| 国内成人精品2018免费看| 欧美亚一区二区| 欧美成人精品1314www| 久久国产精品久久久久久电车| 一区二区三区你懂的| 亚洲第一精品夜夜躁人人爽| 国产精品日韩久久久久| 欧美精品一区二区三区一线天视频| 久久精品91久久香蕉加勒比 | 久久综合九色综合网站| 午夜精品一区二区三区在线 | 欧美资源在线观看| 在线一区二区三区四区| 亚洲国产另类精品专区 | 国产一区二区日韩精品欧美精品| 欧美日韩一卡| 欧美成熟视频| 久久夜色精品国产欧美乱极品 | 久久伊人免费视频| 欧美在线视频一区二区三区| 亚洲一区二区在线免费观看| 亚洲精选在线观看| 亚洲二区在线视频| 激情综合中文娱乐网| 国产一区二区精品| 国产精品一区二区在线观看网站| 欧美日韩国产成人高清视频| 欧美成人xxx| 六十路精品视频| 久久人人超碰| 久久久久成人精品免费播放动漫| 性色av一区二区三区红粉影视| 亚洲午夜一级| 一区二区av在线| 99精品免费| 99在线视频精品| aa级大片欧美| 一本大道av伊人久久综合| av成人免费观看| 一区二区三区波多野结衣在线观看| 亚洲精品黄色| 亚洲精品中文字幕有码专区| 亚洲片区在线| 亚洲美女中出| 亚洲最新在线| 亚洲午夜未删减在线观看| 亚洲一区二区黄| 亚洲欧美日韩国产综合在线| 午夜精品久久久久久99热|