欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Python 存儲(chǔ)字符串時(shí)節(jié)省空間的方法

 更新時(shí)間:2019年04月23日 09:19:01   作者:小小后端  
這篇文章主要介紹了Python 存儲(chǔ)字符串時(shí)節(jié)省空間的方法,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下

從 Python 3 開始,str 類型代表著 Unicode 字符串。取決于編碼的類型,一個(gè) Unicode 字符可能會(huì)占 4 個(gè)字節(jié),這個(gè)有些時(shí)候有點(diǎn)浪費(fèi)內(nèi)存。

出于內(nèi)存占用以及性能方面的考慮,Python 內(nèi)部采用下面 3 種方式來存儲(chǔ) Unicode 字符:

  • 一個(gè)字符占一個(gè)字節(jié)(Latin-1 編碼)
  • 一個(gè)字符占二個(gè)字節(jié)(UCS-2 編碼)
  • 一個(gè)字符占四個(gè)字節(jié)(UCS-4 編碼)

使用 Python 進(jìn)行開發(fā)的時(shí)候,我們會(huì)覺得字符串的處理都很類似,很多時(shí)候根本不需要注意這些差別??墒?,當(dāng)碰到大量的字符處理的時(shí)候,這些細(xì)節(jié)就要特別注意了。

我們可以做一些小實(shí)驗(yàn)來體會(huì)下上面三種方式的差別。方法 sys.getsizeof 用來獲取一個(gè)對(duì)象所占用的字節(jié),這里我們會(huì)用到。

>>> import sys
>>> string = 'hello'
>>> sys.getsizeof(string)
54
>>> # 1-byte encoding
... sys.getsizeof(string + '!') - sys.getsizeof(string)
1
>>> # 2-byte encoding
... string2 = '你'
>>> sys.getsizeof(string2 + '好') - sys.getsizeof(string2)
2
>>> sys.getsizeof(string2)
76
>>> # 4-byte encoding
... string3 = ':snake:'
>>> sys.getsizeof(string3 + ':computer:') - sys.getsizeof(string3)
4
>>> sys.getsizeof(string3)
80

如上所示,當(dāng)字符串的內(nèi)容不同時(shí),所采用的編碼也會(huì)不同。需要注意的是,Python 中每個(gè)字符串都會(huì)另外占用 49-80 字節(jié)的空間,用于存儲(chǔ)額外的一些信息,比如哈希、字符串長(zhǎng)度、字符串字節(jié)數(shù)和字符串標(biāo)識(shí)。這么一來,一個(gè)空字符串會(huì)占用 49 個(gè)字節(jié),也就好理解了。

我們可以通過 cbytes 直接獲取一個(gè)對(duì)象的編碼類型:

import ctypes
class PyUnicodeObject(ctypes.Structure):
 # internal fields of the string object
 _fields_ = [("ob_refcnt", ctypes.c_long),
    ("ob_type", ctypes.c_void_p),
    ("length", ctypes.c_ssize_t),
    ("hash", ctypes.c_ssize_t),
    ("interned", ctypes.c_uint, 2),
    ("kind", ctypes.c_uint, 3),
    ("compact", ctypes.c_uint, 1),
    ("ascii", ctypes.c_uint, 1),
    ("ready", ctypes.c_uint, 1),
    # ...
    # ...
    ]
def get_string_kind(string):
 return PyUnicodeObject.from_address(id(string)).kind

然后測(cè)試

>>> get_string_kind('Hello')
1
>>> get_string_kind('你好')
2
>>> get_string_kind(':snake:')
4

如果一個(gè)字符串中的所有字符都能用 ASCII 表示,那么 Python 會(huì)使用 Latin-1 編碼。簡(jiǎn)單說下,Latin-1 用于表示前 256 個(gè) Unicode 字符。它能支持很多拉丁語言,比如英語、瑞典語、意大利語等。不過,如果是漢語、日語、西伯爾語等非拉丁語言,Latin-1 編碼就行不通了。因?yàn)檫@些語言的文字的碼位值(編碼值)超過了 1 個(gè)字節(jié)的范圍(0-255)。

>>> ord('a')
97
>>> ord('你')
20320
>>> ord('!')
33

大部分語言文字使用 2 個(gè)字節(jié)(UCS-2)來編碼就已經(jīng)足夠了。4 個(gè)字節(jié)(UCS-4)的編碼在保存特殊符號(hào)、emoji 表情或者少見的語言文字的時(shí)候會(huì)用到。

設(shè)想有一個(gè) 10GB 的 ASCII 文本文件,我們準(zhǔn)備將其讀到內(nèi)存里面去。如果你插入一個(gè) emoji 表情到文件中,文件占用空間將會(huì)達(dá)到 4 倍。如果你處理 NLP 問題較多的話,這種差別你應(yīng)該能經(jīng)常體會(huì)到。

Python 內(nèi)部為什么不直接使用 UTF-8 編碼

最常見的 Unicode 編碼是 UTF-8,但是 Python 內(nèi)部并沒有使用它。

UTF-8 編碼字符的時(shí)候,取決于字符的內(nèi)容,占的空間在 1-4 個(gè)字節(jié)內(nèi)發(fā)生變化。這是一種特別省空間的存儲(chǔ)方式,但正因?yàn)檫@種變長(zhǎng)的存儲(chǔ)方式,導(dǎo)致字符串不能通過下標(biāo)直接進(jìn)行隨機(jī)讀取,只能遍歷進(jìn)行查找。比如,如果采用的是 UTF-8 編碼的話,Python 獲取 string[5] 只能一個(gè)一個(gè)字符的進(jìn)行掃描,直至找到目標(biāo)字符。如果是定長(zhǎng)編碼的話也就沒有問題了,要用一個(gè)下標(biāo)定位一個(gè)字符,只需要用下標(biāo)乘以指定長(zhǎng)度(1、2 或者 4)就能確定。

字符串駐留

Python 中的空字符串和 ASCII 字符都會(huì)使用到字符串駐留(string interning)技術(shù)。怎么理解?你就把這些字符(串)看作是單例的就行。也就是說,兩個(gè)相同內(nèi)容的字符串如果使用了駐留的技術(shù),那么內(nèi)存里面其實(shí)就只開辟了一個(gè)空間。

>>> a = 'hello'
>>> b = 'world'
>>> a[4],b[1]
('o', 'o')
>>> id(a[4]), id(b[1]), a[4] is b[1]
(4567926352, 4567926352, True)
>>> id('')
4545673904
>>> id('')
4545673904

正如你看到的那樣,a 中的字符 o 和 b 中的字符 o 有著同樣的內(nèi)存地址。Python 中的字符串是不可修改的,所以提前為某些字符分配好位置便于后面使用也是可行的。

使用到字符串駐留的除了 ASCII 字符、空竄之外,字符長(zhǎng)度不超過 20 的串也使用到了同樣的技術(shù),前提是這些串的內(nèi)容在編譯的時(shí)候就能確定。

這包括:

  • 方法名、類型
  • 變量名
  • 參數(shù)名
  • 常量(代碼中定義的字符串)
  • 字典的鍵
  • 屬性名

當(dāng)你在交互式命令行中編寫代碼的時(shí)候,語句同樣也會(huì)先被編譯成字節(jié)碼。所以說,交互式命令行中的短字符串也會(huì)被駐留。

>>> a = 'teststring'
>>> b = 'teststring'
>>> id(a), id(b), a is b
(4569487216, 4569487216, True)
>>> a = 'test'*5
>>> b = 'test'*5
>>> len(a), id(a), id(b), a is b
(20, 4569499232, 4569499232, True)
>>> a = 'test'*6
>>> b = 'test'*6
>>> len(a), id(a), id(b), a is b
(24, 4569479328, 4569479168, False)

因?yàn)楸仨毷浅A孔址畷?huì)使用到駐留,所以下面的例子不能達(dá)到駐留的效果:

>>> open('test.txt','w').write('hello')
5
>>> open('test.txt','r').read()
'hello'
>>> a = open('test.txt','r').read()
>>> b = open('test.txt','r').read()
>>> id(a), id(b), a is b
(4384934576, 4384934688, False)
>>> len(a), id(a), id(b), a is b
(5, 4384934576, 4384934688, False)

字符串駐留技術(shù),減少了大量的重復(fù)字符串的內(nèi)存分配。Python 底層通過字典實(shí)現(xiàn)的這種技術(shù),這些暫存的字符串作為字典的鍵。如果想要知道某個(gè)字符串是否已經(jīng)駐留,使用字典的查找操作就能確定。

Python 的 unicode 對(duì)象的實(shí)現(xiàn)( https://github.com/python/cpython/blob/master/Objects/unicodeobject.c )大約有 16,000 行 C 代碼,其中有很多小優(yōu)化在本文中未提及。如果你想更多的了解 Python 中的 Unicode,推薦你去看一下字符串相關(guān)的 PEPs( https://www.python.org/dev/peps/ ),同時(shí)查看下 unicode 對(duì)象的源碼。

總結(jié)

以上所述是小編給大家介紹的Python 存儲(chǔ)字符串時(shí)節(jié)省空間的方法,希望對(duì)大家有所幫助,如果大家有任何疑問請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
如果你覺得本文對(duì)你有幫助,歡迎轉(zhuǎn)載,煩請(qǐng)注明出處,謝謝!

相關(guān)文章

  • Python 流程控制實(shí)例代碼

    Python 流程控制實(shí)例代碼

    Python是一門簡(jiǎn)單的語言。對(duì)于一個(gè)問題,應(yīng)該只有一個(gè)解決方法。在Python中,有三種流程控制方法:if-else、while和for。
    2009-09-09
  • python中列表和元組的區(qū)別

    python中列表和元組的區(qū)別

    給大家詳細(xì)講解了python中列表和元組的區(qū)別,需要的朋友參考一下。
    2017-12-12
  • Python使用captcha庫制作帶參數(shù)輸入驗(yàn)證碼案例

    Python使用captcha庫制作帶參數(shù)輸入驗(yàn)證碼案例

    這篇文章主要介紹了Python使用captcha庫制作驗(yàn)證碼,帶參數(shù)輸入,本文通過實(shí)例案例解析給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-05-05
  • python樣條插值的實(shí)現(xiàn)代碼

    python樣條插值的實(shí)現(xiàn)代碼

    這篇文章主要為大家詳細(xì)介紹了python樣條插值的實(shí)現(xiàn)代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-12-12
  • python實(shí)現(xiàn)自動(dòng)登錄人人網(wǎng)并采集信息的方法

    python實(shí)現(xiàn)自動(dòng)登錄人人網(wǎng)并采集信息的方法

    這篇文章主要介紹了python實(shí)現(xiàn)自動(dòng)登錄人人網(wǎng)并采集信息的方法,涉及Python模擬登陸及正則匹配的相關(guān)技巧,需要的朋友可以參考下
    2015-06-06
  • 重溫Python基礎(chǔ)之列表操作

    重溫Python基礎(chǔ)之列表操作

    這篇文章主要帶大家來復(fù)習(xí)一下Python基礎(chǔ)中的列表操作,不知道各位還記得多少呢?文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)Python有一定幫助,需要的可以參考一下
    2022-11-11
  • 跟老齊學(xué)Python之總結(jié)參數(shù)的傳遞

    跟老齊學(xué)Python之總結(jié)參數(shù)的傳遞

    這篇文章主要介紹了Python參數(shù)的傳遞的總結(jié),非常的實(shí)用,有需要的朋友可以參考下
    2014-10-10
  • pycharm 配置遠(yuǎn)程解釋器的方法

    pycharm 配置遠(yuǎn)程解釋器的方法

    今天小編就為大家分享一篇pycharm 配置遠(yuǎn)程解釋器的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2018-10-10
  • python方向鍵控制上下左右代碼

    python方向鍵控制上下左右代碼

    這篇文章主要介紹了python方向鍵控制上下左右代碼,小編覺得還是挺不錯(cuò)的,具有一定借鑒價(jià)值,需要的朋友可以參考下
    2018-01-01
  • python實(shí)現(xiàn)MongoDB的雙活示例

    python實(shí)現(xiàn)MongoDB的雙活示例

    本文主要介紹了python實(shí)現(xiàn)MongoDB的雙活示例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-02-02

最新評(píng)論