Python 字符串存储机制与内存优化详解

一颗松果
一颗松果
发布时间:2026-10-13| 5人浏览过
原创

本文深入解析 Python 3 中 Unicode 字符串的内部存储机制,包括 Latin-1、UCS-2 和 UCS-4 三种编码方式对内存占用的影响。通过 sys.getsizeof 和 cbytes 工具演示如何检测编码类型,并详细阐述字符串驻留(String Interning)技术及其在常量、短字符串中的应用场景,帮助开发者理解并优化 Python 程序中的内存使用效率。

Python 字符串内存编码机制

自 Python 3 起,str 类型专指 Unicode 字符串。根据具体编码方案的不同,单个 Unicode 字符可能占用 4 个字节,这在某些场景下会造成内存资源的浪费。

为了平衡内存占用与运行性能,Python 内部采用以下三种策略来存储 Unicode 字符:

  • 单字节编码(Latin-1)
  • 双字节编码(UCS-2)
  • 四字节编码(UCS-4)

在常规 Python 开发中,字符串处理往往表现一致,开发者通常无需关注底层差异。然而,当涉及大规模字符处理任务时,这些底层细节对性能的影响便不容忽视。

我们可以通过简单的实验来直观感受上述三种编码方式的差异。在此过程中,我们将使用 sys.getsizeof 函数来查询对象所占用的字节数。


>>> 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

如上所示,字符串内容不同,其采用的内部编码方式也不同。值得注意的是,Python 中的每个字符串对象都会额外占用 49 至 80 字节的内存,用于存储哈希值、字符串长度、字节数及标识符等元数据。因此,一个空字符串至少占用 49 字节也就合情合理了。

我们可以借助 cbytes 模块直接获取对象的编码类型标识:


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

随后进行测试:


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

若字符串中的所有字符均可由 ASCII 表示,Python 将自动采用 Latin-1 编码。简言之,Latin-1 用于表示前 256 个 Unicode 字符,支持英语、瑞典语、意大利语等多种拉丁语系语言。但对于汉语、日语、西伯尔语等非拉丁语系文字,由于它们的码位值(编码值)超出了单字节范围(0-255),Latin-1 编码便无法适用。


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

对于大多数语言文字,使用 2 个字节(UCS-2)进行编码已足够。而 4 个字节(UCS-4)编码则主要用于存储特殊符号、Emoji 表情或较为生僻的文字。

假设有一个 10GB 的纯 ASCII 文本文件,若将其加载至内存,并在其中插入一个 Emoji 表情,该文件在内存中的占用空间可能会膨胀至原来的 4 倍。对于经常处理自然语言处理(NLP)问题的开发者而言,这种差异尤为显著。

为何不使用 UTF-8 编码

Python 内部为何不直接使用 UTF-8 编码?

尽管 UTF-8 是最常见的 Unicode 编码格式,但 Python 内部并未采用它。

UTF-8 编码字符时,根据字符内容不同,占用空间在 1 至 4 个字节之间动态变化。这是一种极其节省空间的存储方式,但变长存储的特性导致字符串无法通过下标直接进行随机读取,只能遍历查找。例如,若采用 UTF-8 编码,Python 获取 string[5] 时只能逐个字符扫描直至找到目标。若采用定长编码,则只需将下标乘以指定长度(1、2 或 4)即可直接定位字符,效率更高。

字符串驻留技术详解

字符串驻留机制

Python 中的空字符串和 ASCII 字符均会应用字符串驻留(String Interning)技术。如何理解?你可以将这些字符串视为单例模式。也就是说,如果两个内容相同的字符串都使用了驻留技术,内存中实际上只开辟了一份存储空间。


>>> 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' 拥有相同的内存地址。由于 Python 字符串是不可变的,提前为某些字符分配好内存位置以便后续复用是可行的优化手段。

除了 ASCII 字符和空字符串外,长度不超过 20 的字符串若内容在编译时即可确定,也会应用相同的驻留技术。

具体包括以下场景:

  • 方法名与类型名
  • 变量名
  • 参数名
  • 常量(代码中直接定义的字符串)
  • 字典的键
  • 属性名

当你在交互式命令行中编写代码时,语句会先被编译为字节码。因此,交互式命令行中的短字符串也会被驻留。


>>> 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)

由于驻留机制仅适用于常量字符串,以下示例无法达到驻留效果:


>>> 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)

字符串驻留技术大幅减少了重复字符串的内存分配开销。Python 底层通过字典实现该技术,暂存的字符串作为字典的键。若要判断某个字符串是否已驻留,只需执行字典查找操作即可确认。

Python 的 unicode 对象实现( https://github.com/python/cpython/blob/master/Objects/unicodeobject.c )包含约 16,000 行 C 代码,其中涉及许多未在本文中提及的微优化。若想深入了解 Python 中的 Unicode 处理,建议查阅相关的 PEPs( https://www.python.org/dev/peps/ )以及 unicode 对象的源码。

总结

以上内容介绍了 Python 存储字符串时节省空间的方法,希望对大家有所帮助。如有任何疑问,欢迎留言,小编会及时回复。感谢大家对本站的支持! 若本文对您有帮助,欢迎转载,请注明出处,谢谢!

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。