Python源碼剖析 - Python中的整數對象

来源:https://www.cnblogs.com/xtuz/archive/2020/03/22/12548887.html
-Advertisement-
Play Games

1. 不可變的PyIntObject "Python源碼剖析 對象初探" 我們對 PyIntObject 已經有了初步的瞭解。 Python 中的對象可以分為固定長度和可變長度兩種類型。除此之外,也可以按照可變和不可變進行劃分。 PyIntObject 則屬於長度固定且不可變的對象。相比其他的對象而 ...


1. 不可變的PyIntObject

Python源碼剖析 - 對象初探 我們對 PyIntObject 已經有了初步的瞭解。 Python 中的對象可以分為固定長度和可變長度兩種類型。除此之外,也可以按照可變和不可變進行劃分。

PyIntObject 則屬於長度固定且不可變的對象。相比其他的對象而言,最簡單,也最容易理解。

我們先來瞭解一下 PyIntObject 類型的類型信息,代碼如下:

PyTypeObject PyInt_Type = {
    PyVarObject_HEAD_INIT(&PyType_Type, 0)
    "int",
    sizeof(PyIntObject),
    0,
    (destructor)int_dealloc,                    /* tp_dealloc */
    (printfunc)int_print,                       /* tp_print */
    0,                                          /* tp_getattr */
    0,                                          /* tp_setattr */
    (cmpfunc)int_compare,                       /* tp_compare */
    (reprfunc)int_to_decimal_string,            /* tp_repr */
    &int_as_number,                             /* tp_as_number */
    0,                                          /* tp_as_sequence */
    0,                                          /* tp_as_mapping */
    (hashfunc)int_hash,                         /* tp_hash */
    0,                                          /* tp_call */
    (reprfunc)int_to_decimal_string,            /* tp_str */
    PyObject_GenericGetAttr,                    /* tp_getattro */
    0,                                          /* tp_setattro */
    0,                                          /* tp_as_buffer */
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES |
        Py_TPFLAGS_BASETYPE | Py_TPFLAGS_INT_SUBCLASS,          /* tp_flags */
    int_doc,                                    /* tp_doc */
    0,                                          /* tp_traverse */
    0,                                          /* tp_clear */
    0,                                          /* tp_richcompare */
    0,                                          /* tp_weaklistoffset */
    0,                                          /* tp_iter */
    0,                                          /* tp_iternext */
    int_methods,                                /* tp_methods */
    0,                                          /* tp_members */
    int_getset,                                 /* tp_getset */
    0,                                          /* tp_base */
    0,                                          /* tp_dict */
    0,                                          /* tp_descr_get */
    0,                                          /* tp_descr_set */
    0,                                          /* tp_dictoffset */
    0,                                          /* tp_init */
    0,                                          /* tp_alloc */
    int_new,                                    /* tp_new */
};

核心代碼解釋:

代碼 說明
PyVarObject_HEAD_INIT(&PyType_Type, 0) 1. 設定ob_type指向PyInt_Type結構的地址 2.定長
"int" 設定 tp_name
int_dealloc PyIntObject對象的析構函數
int_print PyIntObject對象的標準輸出函數
int_compare 比較操作
int_to_decimal_string 將整數轉換為數字字元串
&int_as_number 數學操作函數集合,如加減乘除等
int_hash 計算該對象的hash值
int_methods 對象成員函數的集合

2. PyIntObject對象創建三種方式

關於 PyIntObject 的對象創建過程,我們在Python源碼剖析 - 對象初探中已經做了初步的介紹,通過閱讀源碼我們可以發現有有以下三種方式,可以用來創建 PyIntObject 對象

PyAPI_FUNC(PyObject *) PyInt_FromString(char*, char**, int);
PyAPI_FUNC(PyObject *) PyInt_FromUnicode(Py_UNICODE*, Py_ssize_t, int);
PyAPI_FUNC(PyObject *) PyInt_FromLong(long);

也就是說,一個 PyIntObject 可以來源於 String、Unicode 和 Long 類型的變數。

PyObject *
PyInt_FromLong(long ival)
{
    register PyIntObject *v;
#if NSMALLNEGINTS + NSMALLPOSINTS > 0
    if (-NSMALLNEGINTS <= ival && ival < NSMALLPOSINTS) {
        v = small_ints[ival + NSMALLNEGINTS];
        Py_INCREF(v);
#ifdef COUNT_ALLOCS
        if (ival >= 0)
            quick_int_allocs++;
        else 
            quick_neg_int_allocs++;
#endif
        return (PyObject *) v;
    }    
#endif
    if (free_list == NULL) {
        if ((free_list = fill_free_list()) == NULL)
            return NULL;
    }    
    /* Inline PyObject_New */
    v = free_list;
    free_list = (PyIntObject *)Py_TYPE(v);
    (void)PyObject_INIT(v, &PyInt_Type);
    v->ob_ival = ival;
    return (PyObject *) v;
}

從代碼實現來看,如果是一個小整數,那麼就直接增加對這個小整數對象的引用,否則,則需要從 free_list 中選取一個可用的對象,並將該對象的 ob_ival 設置為當前的數值。

接下來,我們詳細介紹 PyIntObject 中對小整數的處理方式。

3. PyIntObject的小整數對象

在 Python 中,代碼直接對小整數對象的範圍進行了限定,即 [-5, 257)

#ifndef NSMALLPOSINTS
#define NSMALLPOSINTS           257
#endif
#ifndef NSMALLNEGINTS
#define NSMALLNEGINTS           5
#endif
#if NSMALLNEGINTS + NSMALLPOSINTS > 0
/* References to small integers are saved in this array so that they
   can be shared.
   The integers that are saved are those in the range
   -NSMALLNEGINTS (inclusive) to NSMALLPOSINTS (not inclusive).
*/
static PyIntObject *small_ints[NSMALLNEGINTS + NSMALLPOSINTS];
#endif

這些小整數對象,類似於常量一樣常駐記憶體中,並不會不釋放,這樣做的優點在於:

  • 使用效率高,這些小整數對象,像靜態常量一樣,直接拿來就可以用
  • 避免經常使用小整數導致記憶體操作效率降低 - 假設我們沒有將小整數常駐記憶體,按照 Python 中其他對象的處理方式來處理,必然會導致 malloc() 和 free() 的頻繁調用,引起大量記憶體碎片,嚴重影響 Python 的整體性

但是這樣做有幾個的問題:

  • 常量的設定,是一個經驗值,你沒辦法預計在你的程式里,這個樣的設置就是最優的
  • 修改代價大,如果你發現我對小整數的範圍進行調整,你能做的唯一辦法,就是對源碼進行修改,併進行重新編譯。

4. PyIntObject的大整數對象

對於小整數,Python 通過小整數對象池的方式來解決效率問題,那麼對於其他整數對象,又是如何處理的呢。

其實與小整數類似,也是通過記憶體池技術,不同的是這個記憶體池中的數值並不是固定的,而是誰需要使用,就來申請,使用完了,則歸還到池子中去。

struct _intblock {
    struct _intblock *next;
    PyIntObject objects[N_INTOBJECTS];
};

typedef struct _intblock PyIntBlock;

static PyIntBlock *block_list = NULL;
static PyIntObject *free_list = NULL;

static PyIntObject *
fill_free_list(void)
{
    PyIntObject *p, *q;
    /* Python's object allocator isn't appropriate for large blocks. */
    p = (PyIntObject *) PyMem_MALLOC(sizeof(PyIntBlock));
    if (p == NULL)
        return (PyIntObject *) PyErr_NoMemory();
    ((PyIntBlock *)p)->next = block_list;
    block_list = (PyIntBlock *)p;
    /* Link the int objects together, from rear to front, then return
       the address of the last int object in the block. */
    p = &((PyIntBlock *)p)->objects[0];
    q = p + N_INTOBJECTS;
    while (--q > p)
        Py_TYPE(q) = (struct _typeobject *)(q-1);
    Py_TYPE(q) = NULL;
    return p + N_INTOBJECTS - 1;
}

通過 block_listfree_list 兩個指針來進行維護,free_list 是一個單向列表,維護 block_list 中所有可用的記憶體塊。如果 block_list 不夠用了,則調用 fill_free_list() 申請新的記憶體。

5. 更多內容

原文來自兔子先生網站:https://www.xtuz.net/detail-136.html

查看原文 >>> Python源碼剖析 - Python中的整數對象

如果你對Python語言感興趣,可以關註我,或者關註我的微信公眾號:xtuz666


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、java.io.DataOutputStream;數據位元組輸出流 1.可以將記憶體中的“int i = 2;"寫入到硬碟文件裡面,寫進去的不是字元串,寫進去的是二進位數據,可以帶有類型。 package com.bjpowernode.java_learning; import java.io.* ...
  • 以前看到過NSQ這個東西,也一直沒去看。今天剛好有時間就搭建了下,簡單嘗試了下這個Go語言下的消息隊列NSQ,我這裡簡要記錄下。 其實,NSQ國內用的是比較少的,我這裡也是算瞭解這麼個東西吧 ,稍微看下源碼,學到東西而已。 NSQ簡介 NSQ是一個基於Go語言的分散式實時消息平臺, 它具有分散式、去 ...
  • 現在springboot的火熱程度已經超過了spring了,因為springboot簡單快速方便,springboot的初衷就是為了簡化spring的配置,是的開發中集成新功能時更快,簡化或者減少相關的配置。springboot的基礎是“約定大於配置”。整合了所有的框架,可以把springboot當 ...
  • ...
  • [TOC] 環境 idea 2019.1 Meavn 3.6.0 SpringBoot 2.2.5 jdk 1.8 構建eureka server 新建工程 啟動類添加註解 @EnableEurekaServer 其他配置 構建eureka client 新建工程 pom文件添加依賴,解決啟動失敗 ...
  • 距離Java 8發佈已經過去了7、8年的時間,Java 14也剛剛發佈。Java 8中關於函數式編程和新增的Stream流API至今飽受“爭議”。 如果你不曾使用Stream流,那麼當你見到Stream操作時一定對它發出過鄙夷的聲音,併在心裡說出“這都寫的什麼玩意兒”。 如果你熱衷於使用Stream ...
  • 恢復內容開始 1.背景:現在很多app或者網站都想要接入微信登錄,可以使用戶不需要註冊就能快速使用APP或網站。 2.微信登錄需要一些前置操作 2.1 搜索:微信開放平臺 鏈接:https://open.weixin.qq.com/ 2.2 註冊成功,獲取到開發所需要的appID和appsecret ...
  • elastic4s是elasticsearch一個第三方開發的scala語言終端工具庫(Elastic4s is a concise, idiomatic, reactive, type safe Scala client for Elasticsearch.)。scala用戶可以用elastic4 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...