本系列作为本人@takooctopus深入学习python机制的记录,这个博客遵照着栖迟于一丘的博客上面的流程进行的,也包含我在实际查看源码时的感想,特此列出,表示感谢。
关于python3
在看任何语言的字符串之前,我们首先应该看其编码问题。
python3中默认使用unicode编码,我们找到它的定义:
{{- code -}}
在[Objects/unicodeoject.c]
中,提供了许多创建unicode对象的方法。我们考虑字符串,所以寻找PyUnicode_FromString()
{{- code -}}
在这里,其设置了一个数据的上限PY_SSIZE_T_MAX
,其值为((Py_ssize_t)(((size_t)-1)>>1))
,考虑我们64位系统,size_t
为__int64
。
明显的,当其没有溢出时,就转入下一步。
{{- code -}}
PyUnicode_DecodeUTF8Stateful()
这个函数就会自动判断格式。
字符串对象共享机制
我们在之前讨论过了小整数的机制,其预先实现了小整数的dict,再对其进行引用。
字符串也同样有这种类似的引用机制itern
{{- code -}}
传入的字符串首先经过类型和状态检查PyUnicode_Check()
、PyUnicode_CheckExact()
、PyUnicode_CHECK_INTERNED()
。如果都通过,且没有被interned
,就新建dict。
特别的Py_REFCNT(s) -= 2
,由于对象添加进入dict时会对对其引用进行两次+1
操作,所以需要对引用-2
平衡。
注意其仍旧是生成了一个临时字符串对象,在引用交接后将其销毁了。
以c的字符串创建一样需要创建对象:
{{- code -}}
我们看见字符串对象有属性interned
,在经历了PyUnicode_InternInPlace()
后会改变,其值有两种选择SSTATE_INTERNED_MORTAL
和SSTATE_INTERNED_IMMORTAL
,分别对应了其是否会被销毁。
我们可以在PyUnicode_InternImmortal()
中更改状态。
{{- code -}}
字符缓冲池
单个unicode字符就直接以一个缓冲池保存
{{- code -}}
先对所创建的字符串(只有一个字符)对象进行intern操作,再将inter的结果缓存到字符缓冲池unicode_latin1
中,两者都是指向同一个字符对象。
PyUnicodeObject拼接
我们上面讲了每个PyUnicodeObject对象都是一个不可变对象。我们使用+
来拼接字符串会导致n-1
次的内存搬运工作。
{{- code -}}
官方推荐使用join
操作。
{{- code -}}
再看PyUnicode_Join()
:
{{- code -}}
再我们看_PyUnicode_JoinArray()
的实现:
{{- code -}}
其在统计了list中的对象及长度后,统一进行申请空间并逐一进行拷贝。