国产av日韩一区二区三区精品,成人性爱视频在线观看,国产,欧美,日韩,一区,www.成色av久久成人,2222eeee成人天堂

目錄
1 Unicode
2 Python中的Unicode
#2.1 Unicode物件的好處
2.2 Python對Unicode的最佳化
3 Unicode對象的底層結(jié)構(gòu)體
3.1 PyASCIIObject
3.2 PyCompactUnicodeObject
3.3 PyUnicodeObject
3.4 示例
4 interned機(jī)制
首頁 後端開發(fā) Python教學(xué) Python內(nèi)建類型str原始碼分析

Python內(nèi)建類型str原始碼分析

May 09, 2023 pm 02:16 PM
python str

1 Unicode

電腦儲存的基本單位是位元組,由8個位元位元組成。由於英文只由26個字母加若干符號組成,因此英文字元可以直接用位元組來保存。但是其他語言(例如中日韓等),由於字元眾多,不得不使用多個位元組來進(jìn)行編碼。

隨著電腦科技的傳播,非拉丁文字元編碼技術(shù)不斷發(fā)展,但仍有兩個比較大的限制:

  • 不支援多語言:一種語言的編碼方案不能用於另一種語言

  • 沒有統(tǒng)一標(biāo)準(zhǔn):例如中文就有GBK、GB2312、GB18030等多種編碼標(biāo)準(zhǔn)

#由於編碼方式不統(tǒng)一,開發(fā)人員就需要在不同編碼之間來回轉(zhuǎn)換,不可避免地會出現(xiàn)很多錯誤。為了解決這類不統(tǒng)一問題,Unicode標(biāo)準(zhǔn)被提出了。 Unicode會對世界上大部分文字系統(tǒng)進(jìn)行整理、編碼,讓電腦可以用統(tǒng)一的方式處理文字。 Unicode目前已經(jīng)收錄了超過14萬個字符,自然支援多語言。 (Unicode的uni就是「統(tǒng)一」的字根)

2 Python中的Unicode

#2.1 Unicode物件的好處

Python在3之後,str物件內(nèi)部改用Unicode表示,因此在原始碼中成為Unicode物件。使用Unicode表示的好處是:程式核心邏輯統(tǒng)一使用Unicode,只需在輸入、輸出層進(jìn)行解碼、編碼,可最大程度地避免各種編碼問題。

圖示如下:

Python內(nèi)建類型str原始碼分析

2.2 Python對Unicode的最佳化

#問題:由於Unicode收錄字元已經(jīng)超過14萬個,每個字元至少要4個位元組來保存(這裡應(yīng)該是因?yàn)?個位元組不夠,所以才用4個位元組,一般不會使用3個位元組)。而英文字元用ASCII碼表示只需要1個字節(jié),使用Unicode反而會使頻繁使用的英文字元的開銷變成原來的4倍。

首先我們來看看Python中不同形式的str物件的大小差異:

>>> sys.getsizeof('ab') - sys.getsizeof('a')
1
>>> sys.getsizeof('一二') - sys.getsizeof('一')
2
>>> sys.getsizeof('????????') - sys.getsizeof('????')
4

由此可見,Python內(nèi)部對Unicode物件進(jìn)行了最佳化:根據(jù)文字內(nèi)容,選擇底層儲存單元。

Unicode物件底層儲存根據(jù)文字字元的Unicode碼位元範(fàn)圍分成三類:

  • PyUnicode_1BYTE_KIND:所有字元碼位元在U 0000到U 00FF之間

  • PyUnicode_2BYTE_KIND:所有字元碼位元在U 0000到U FFFF之間,且至少有一個字元的碼位元大於U 00FF

  • PyUnicode_1BYTE_KIND:所有字元碼位在U 0000到U 10FFFF之間,且至少有一個字元的碼位大於U FFFF

對應(yīng)枚舉如下:

enum PyUnicode_Kind {
/* String contains only wstr byte characters.  This is only possible
   when the string was created with a legacy API and _PyUnicode_Ready()
   has not been called yet.  */
    PyUnicode_WCHAR_KIND = 0,
/* Return values of the PyUnicode_KIND() macro: */
    PyUnicode_1BYTE_KIND = 1,
    PyUnicode_2BYTE_KIND = 2,
    PyUnicode_4BYTE_KIND = 4
};

根據(jù)不同的分類,選擇不同的儲存單元:

/* Py_UCS4 and Py_UCS2 are typedefs for the respective
   unicode representations. */
typedef uint32_t Py_UCS4;
typedef uint16_t Py_UCS2;
typedef uint8_t Py_UCS1;

對應(yīng)關(guān)係如下:

##字元儲存單元字元儲存單元大?。ㄎ辉M)PyUnicode_1BYTE_KINDPy_UCS11#PyUnicode_2BYTE_KIND1#PyUnicode_2BYTE_KIND#PyUnicode_2BYTE_KIND1
#文字類型
1#PyUnicode_2BYTE_KIND
1

    #PyUnicode_2BYTE_KIND
  • 1

  • #PyUnicode_2BYTE_KIND

    1

  • #PyUnicode

  • Py_UCS2
  • 2

PyUnicode_4BYTE_KIND

Py_UCS4#4#Py_UCS4#4#由於Unicode內(nèi)部儲存結(jié)構(gòu)因文字類型而異,因此類型kind必須作為Unicode物件公共欄位進(jìn)行儲存。 Python內(nèi)部定義了一些標(biāo)誌位,作為Unicode公共字段:(介於筆者水平有限,這裡的字段在後續(xù)內(nèi)容中不會全部介紹,大家後續(xù)可以自行了解。抱拳~)##compact:記憶體分配方式,物件與文字緩衝區(qū)是否分離asscii:文字是否皆為純ASCII透過PyUnicode_New函數(shù),根據(jù)文字字元數(shù)size以及最大字元maxchar初始化Unicode物件。此函數(shù)主要是根據(jù)maxchar為Unicode物件選擇最緊湊的字元儲存單元以及底層結(jié)構(gòu)體:(原始碼比較長,這裡就不列出了,大家可以自行了解,下面以表格形式展現(xiàn))maxchar < 128kindascii
#interned:是否為interned機(jī)制維護(hù)kind:類型,用於區(qū)分字元底層儲存單元大小
?
#128 <= maxchar < 256256 <= maxchar < 65536 65536 <= maxchar < MAX_UNICODE
PyUnicode_1BYTE_KINDPyUnicode_1BYTE_KIND PyUnicode_2BYTE_KINDPyUnicode_4BYTE_KIND
#1######0######0#####0# ###########字元儲存單元大?。ㄎ辉M)######1#######1######2######4### #########底層結(jié)構(gòu)體######PyASCIIObject######PyCompactUnicodeObject######PyCompactUnicodeObject#######PyCompactUnicodeObject########## ##

3 Unicode對象的底層結(jié)構(gòu)體

3.1 PyASCIIObject

C源碼:

typedef struct {
    PyObject_HEAD
    Py_ssize_t length;          /* Number of code points in the string */
    Py_hash_t hash;             /* Hash value; -1 if not set */
    struct {
        unsigned int interned:2;
        unsigned int kind:3;
        unsigned int compact:1;
        unsigned int ascii:1;
        unsigned int ready:1;
        unsigned int :24;
    } state;
    wchar_t *wstr;              /* wchar_t representation (null-terminated) */
} PyASCIIObject;

源碼分析:

length:文本長度

hash:文本哈希值

state:Unicode對象標(biāo)志位

wstr:緩存C字符串的一個wchar_t指針,以“\0”結(jié)束(這里和我看的另一篇文章講得不太一樣,另一個描述是:ASCII文本緊接著位于PyASCIIObject結(jié)構(gòu)體后面,我個人覺得現(xiàn)在的這種說法比較準(zhǔn)確,畢竟源碼結(jié)構(gòu)體后面沒有別的字段了)

圖示如下:

(注意這里state字段后面有一個4字節(jié)大小的空洞,這是結(jié)構(gòu)體字段內(nèi)存對齊造成的現(xiàn)象,主要是為了優(yōu)化內(nèi)存訪問效率)

Python內(nèi)建類型str原始碼分析

ASCII文本由wstr指向,以’abc’和空字符串對象’'為例:

Python內(nèi)建類型str原始碼分析

Python內(nèi)建類型str原始碼分析

3.2 PyCompactUnicodeObject

如果文本不全是ASCII,Unicode對象底層便由PyCompactUnicodeObject結(jié)構(gòu)體保存。C源碼如下:

/* Non-ASCII strings allocated through PyUnicode_New use the
   PyCompactUnicodeObject structure. state.compact is set, and the data
   immediately follow the structure. */
typedef struct {
    PyASCIIObject _base;
    Py_ssize_t utf8_length;     /* Number of bytes in utf8, excluding the
                                 * terminating \0. */
    char *utf8;                 /* UTF-8 representation (null-terminated) */
    Py_ssize_t wstr_length;     /* Number of code points in wstr, possible
                                 * surrogates count as two code points. */
} PyCompactUnicodeObject;

PyCompactUnicodeObject在PyASCIIObject的基礎(chǔ)上增加了3個字段:

utf8_length:文本UTF8編碼長度

utf8:文本UTF8編碼形式,緩存以避免重復(fù)編碼運(yùn)算

wstr_length:wstr的“長度”(這里所謂的長度沒有找到很準(zhǔn)確的說法,筆者也不太清楚怎么能打印出來,大家可以自行研究下)

注意到,PyASCIIObject中并沒有保存UTF8編碼形式,這是因?yàn)锳SCII本身就是合法的UTF8,這也是ASCII文本底層由PyASCIIObject保存的原因。

結(jié)構(gòu)圖示:

Python內(nèi)建類型str原始碼分析

3.3 PyUnicodeObject

PyUnicodeObject則是Python中str對象的具體實(shí)現(xiàn)。C源碼如下:

/* Strings allocated through PyUnicode_FromUnicode(NULL, len) use the
   PyUnicodeObject structure. The actual string data is initially in the wstr
   block, and copied into the data block using _PyUnicode_Ready. */
typedef struct {
    PyCompactUnicodeObject _base;
    union {
        void *any;
        Py_UCS1 *latin1;
        Py_UCS2 *ucs2;
        Py_UCS4 *ucs4;
    } data;                     /* Canonical, smallest-form Unicode buffer */
} PyUnicodeObject;

3.4 示例

在日常開發(fā)時,要結(jié)合實(shí)際情況注意字符串拼接前后的內(nèi)存大小差別:

>>> import sys
>>> text = &#39;a&#39; * 1000
>>> sys.getsizeof(text)
1049
>>> text += &#39;????&#39;
>>> sys.getsizeof(text)
4080

4 interned機(jī)制

如果str對象的interned標(biāo)志位為1,Python虛擬機(jī)將為其開啟interned機(jī)制,

源碼如下:(相關(guān)信息在網(wǎng)上可以看到很多說法和解釋,這里筆者能力有限,暫時沒有找到最確切的答案,之后補(bǔ)充。抱拳~但是我們通過分析源碼應(yīng)該是能看出一些門道的)

/* This dictionary holds all interned unicode strings.  Note that references
   to strings in this dictionary are *not* counted in the string&#39;s ob_refcnt.
   When the interned string reaches a refcnt of 0 the string deallocation
   function will delete the reference from this dictionary.
   Another way to look at this is that to say that the actual reference
   count of a string is:  s->ob_refcnt + (s->state ? 2 : 0)
*/
static PyObject *interned = NULL;
void
PyUnicode_InternInPlace(PyObject **p)
{
    PyObject *s = *p;
    PyObject *t;
#ifdef Py_DEBUG
    assert(s != NULL);
    assert(_PyUnicode_CHECK(s));
#else
    if (s == NULL || !PyUnicode_Check(s))
        return;
#endif
    /* If it&#39;s a subclass, we don&#39;t really know what putting
       it in the interned dict might do. */
    if (!PyUnicode_CheckExact(s))
        return;
    if (PyUnicode_CHECK_INTERNED(s))
        return;
    if (interned == NULL) {
        interned = PyDict_New();
        if (interned == NULL) {
            PyErr_Clear(); /* Don&#39;t leave an exception */
            return;
        }
    }
    Py_ALLOW_RECURSION
    t = PyDict_SetDefault(interned, s, s);
    Py_END_ALLOW_RECURSION
    if (t == NULL) {
        PyErr_Clear();
        return;
    }
    if (t != s) {
        Py_INCREF(t);
        Py_SETREF(*p, t);
        return;
    }
    /* The two references in interned are not counted by refcnt.
       The deallocator will take care of this */
    Py_REFCNT(s) -= 2;
    _PyUnicode_STATE(s).interned = SSTATE_INTERNED_MORTAL;
}

可以看到,源碼前面還是做一些基本的檢查。我們可以看一下37行和50行:將s添加到interned字典中時,其實(shí)s同時是key和value(這里我不太清楚為什么會這樣做),所以s對應(yīng)的引用計(jì)數(shù)是+2了的(具體可以看PyDict_SetDefault()的源碼),所以在50行時會將計(jì)數(shù)-2,保證引用計(jì)數(shù)的正確。

考慮下面的場景:

>>> class User:
    def __init__(self, name, age):
        self.name = name
        self.age = age
>>> user = User(&#39;Tom&#39;, 21)
>>> user.__dict__
{&#39;name&#39;: &#39;Tom&#39;, &#39;age&#39;: 21}

由于對象的屬性由dict保存,這意味著每個User對象都要保存一個str對象‘name’,這會浪費(fèi)大量的內(nèi)存。而str是不可變對象,因此Python內(nèi)部將有潛在重復(fù)可能的字符串都做成單例模式,這就是interned機(jī)制。Python具體做法就是在內(nèi)部維護(hù)一個全局dict對象,所有開啟interned機(jī)制的str對象均保存在這里,后續(xù)需要使用的時候,先創(chuàng)建,如果判斷已經(jīng)維護(hù)了相同的字符串,就會將新創(chuàng)建的這個對象回收掉。

示例:

由不同運(yùn)算生成’abc’,最后都是同一個對象:

>>> a = &#39;abc&#39;
>>> b = &#39;ab&#39; + &#39;c&#39;
>>> id(a), id(b), a is b
(2752416949872, 2752416949872, True)

以上是Python內(nèi)建類型str原始碼分析的詳細(xì)內(nèi)容。更多資訊請關(guān)注PHP中文網(wǎng)其他相關(guān)文章!

本網(wǎng)站聲明
本文內(nèi)容由網(wǎng)友自願投稿,版權(quán)歸原作者所有。本站不承擔(dān)相應(yīng)的法律責(zé)任。如發(fā)現(xiàn)涉嫌抄襲或侵權(quán)的內(nèi)容,請聯(lián)絡(luò)admin@php.cn

熱AI工具

Undress AI Tool

Undress AI Tool

免費(fèi)脫衣圖片

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅(qū)動的應(yīng)用程序,用於創(chuàng)建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費(fèi)的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費(fèi)的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強(qiáng)大的PHP整合開發(fā)環(huán)境

Dreamweaver CS6

Dreamweaver CS6

視覺化網(wǎng)頁開發(fā)工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

如何處理Python中的API身份驗(yàn)證 如何處理Python中的API身份驗(yàn)證 Jul 13, 2025 am 02:22 AM

處理API認(rèn)證的關(guān)鍵在於理解並正確使用認(rèn)證方式。 1.APIKey是最簡單的認(rèn)證方式,通常放在請求頭或URL參數(shù)中;2.BasicAuth使用用戶名和密碼進(jìn)行Base64編碼傳輸,適合內(nèi)部系統(tǒng);3.OAuth2需先通過client_id和client_secret獲取Token,再在請求頭中帶上BearerToken;4.為應(yīng)對Token過期,可封裝Token管理類自動刷新Token;總之,根據(jù)文檔選擇合適方式,並安全存儲密鑰信息是關(guān)鍵。

如何用Python測試API 如何用Python測試API Jul 12, 2025 am 02:47 AM

要測試API需使用Python的Requests庫,步驟為安裝庫、發(fā)送請求、驗(yàn)證響應(yīng)、設(shè)置超時與重試。首先通過pipinstallrequests安裝庫;接著用requests.get()或requests.post()等方法發(fā)送GET或POST請求;然後檢查response.status_code和response.json()確保返回結(jié)果符合預(yù)期;最後可添加timeout參數(shù)設(shè)置超時時間,並結(jié)合retrying庫實(shí)現(xiàn)自動重試以增強(qiáng)穩(wěn)定性。

Python函數(shù)可變範(fàn)圍 Python函數(shù)可變範(fàn)圍 Jul 12, 2025 am 02:49 AM

在Python中,函數(shù)內(nèi)部定義的變量是局部變量,僅在函數(shù)內(nèi)有效;外部定義的是全局變量,可在任何地方讀取。 1.局部變量隨函數(shù)執(zhí)行結(jié)束被銷毀;2.函數(shù)可訪問全局變量但不能直接修改,需用global關(guān)鍵字;3.嵌套函數(shù)中若要修改外層函數(shù)變量,需使用nonlocal關(guān)鍵字;4.同名變量在不同作用域互不影響;5.修改全局變量時必須聲明global,否則會引發(fā)UnboundLocalError錯誤。理解這些規(guī)則有助於避免bug並寫出更可靠的函數(shù)。

Python Fastapi教程 Python Fastapi教程 Jul 12, 2025 am 02:42 AM

要使用Python創(chuàng)建現(xiàn)代高效的API,推薦使用FastAPI;其基於標(biāo)準(zhǔn)Python類型提示,可自動生成文檔,性能優(yōu)越。安裝FastAPI和ASGI服務(wù)器uvicorn後,即可編寫接口代碼。通過定義路由、編寫處理函數(shù)並返回?cái)?shù)據(jù),可以快速構(gòu)建API。 FastAPI支持多種HTTP方法,並提供自動生成的SwaggerUI和ReDoc文檔系統(tǒng)。 URL參數(shù)可通過路徑定義捕獲,查詢參數(shù)則通過函數(shù)參數(shù)設(shè)置默認(rèn)值實(shí)現(xiàn)。合理使用Pydantic模型有助於提升開發(fā)效率和準(zhǔn)確性。

與超時的python循環(huán) 與超時的python循環(huán) Jul 12, 2025 am 02:17 AM

為Python的for循環(huán)添加超時控制,1.可結(jié)合time模塊記錄起始時間,在每次迭代中判斷是否超時並使用break跳出循環(huán);2.對於輪詢類任務(wù),可用while循環(huán)配合時間判斷,並加入sleep避免CPU佔(zhàn)滿;3.進(jìn)階方法可考慮threading或signal實(shí)現(xiàn)更精確控制,但複雜度較高,不建議初學(xué)者首選;總結(jié)關(guān)鍵點(diǎn):手動加入時間判斷是基本方案,while更適合限時等待類任務(wù),sleep不可缺失,高級方法適用於特定場景。

如何在Python中解析大型JSON文件? 如何在Python中解析大型JSON文件? Jul 13, 2025 am 01:46 AM

如何在Python中高效處理大型JSON文件? 1.使用ijson庫流式處理,通過逐項(xiàng)解析避免內(nèi)存溢出;2.若為JSONLines格式,可逐行讀取並用json.loads()處理;3.或先將大文件拆分為小塊再分別處理。這些方法有效解決內(nèi)存限制問題,適用於不同場景。

python循環(huán)在元組上 python循環(huán)在元組上 Jul 13, 2025 am 02:55 AM

在Python中,用for循環(huán)遍曆元組的方法包括直接迭代元素、同時獲取索引和元素、以及處理嵌套元組。 1.直接使用for循環(huán)可依次訪問每個元素,無需管理索引;2.使用enumerate()可同時獲取索引和值,默認(rèn)索引起始為0,也可指定start參數(shù);3.對嵌套元組可在循環(huán)中解包,但需確保子元組結(jié)構(gòu)一致,否則會引發(fā)解包錯誤;此外,元組不可變,循環(huán)中不能修改內(nèi)容,可用\_忽略不需要的值,且建議遍歷前檢查元組是否為空以避免錯誤。

Python默認(rèn)論點(diǎn)及其潛在問題是什麼? Python默認(rèn)論點(diǎn)及其潛在問題是什麼? Jul 12, 2025 am 02:39 AM

Python默認(rèn)參數(shù)在函數(shù)定義時評估並固定值,可能導(dǎo)致意外問題。使用可變對像如列表作為默認(rèn)參數(shù)會保留修改,建議用None代替;默認(rèn)參數(shù)作用域是定義時的環(huán)境變量,後續(xù)變量變化不影響其值;避免依賴默認(rèn)參數(shù)保存狀態(tài),應(yīng)使用類封裝狀態(tài)以確保函數(shù)一致性。

See all articles