正在看Robert Chen 大神的《Python 源码剖析》,关于第三章的字符串对象有两个疑问。

185 views
Skip to first unread message

jamie

unread,
Aug 16, 2018, 11:51:12 PM8/16/18
to python-cn(华蟒用户组,CPyUG 邮件列表)
1. 字符串缓冲池

在 stringobject.c 中定义了 characters 数组用于缓存长度为1的字符串

static PyStringObject *characters[UCHAR_MAX + 1];  //UCHAR_MAX =255

当创建字符串时,如果characters已经存在该字符串,则直接返回characters中的字符串,可避免内存中充斥大量长度为1的字符串。

我实际测试发现characters似乎不会缓存'*','&'之类的特殊字符。

测试方法:
1. 在string_length 函数中打印出characters
2. 在 Python shell 中执行
a = '*'
len
(a)
3. '*'不在打印的characters中

不知道 Python 内部是如何使用 characters 的?

2. intern 机制

我在查看stringobject.c 的代码时,发现只有当字符串长度为0或者1时才会经过 intern 机制处理:

PyObject *
PyString_FromString(const char *str)
{
    ...
    /* share short strings */
    if (size == 0) {
        PyObject *t = (PyObject *)op;
        PyString_InternInPlace(&t);
        op = (PyStringObject *)t;
        nullstring = op;
        Py_INCREF(op);
    } else if (size == 1) {
        PyObject *t = (PyObject *)op;
        PyString_InternInPlace(&t);
        op = (PyStringObject *)t;
        characters[*str & UCHAR_MAX] = op;
        Py_INCREF(op);
    }
    return (PyObject *) op;
}

实际测试是长度超过1的字符串也会经过 intern 机制处理,不知道是否在别的地方调用的了 intern 机制?

Leo Jay

unread,
Aug 17, 2018, 12:57:58 AM8/17/18
to python-cn:CPyUG
On Thu, Aug 16, 2018 at 8:51 PM jamie <ljm5...@gmail.com> wrote:
>
> 1. 字符串缓冲池
>
> 在 stringobject.c 中定义了 characters 数组用于缓存长度为1的字符串
>
> static PyStringObject *characters[UCHAR_MAX + 1]; //UCHAR_MAX =255
>
> 当创建字符串时,如果characters已经存在该字符串,则直接返回characters中的字符串,可避免内存中充斥大量长度为1的字符串。
>
> 我实际测试发现characters似乎不会缓存'*','&'之类的特殊字符。
>
> 测试方法:
> 1. 在string_length 函数中打印出characters
> 2. 在 Python shell 中执行
> a = '*'
> len(a)
> 3. '*'不在打印的characters中
>
> 不知道 Python 内部是如何使用 characters 的?
>

我把 string_length 改成这样:
static Py_ssize_t
string_length(PyStringObject *a)
{
PyStringObject* p = characters['*'];
if (p) {
printf("DBG: Found: %s\n", PyString_AsString(p));
} else {
printf("DBG: Not Found\n");
}
return Py_SIZE(a);
}

运行结果:
>>> a = '*'
>>> len(a)
DBG: Found: *
1
>>>

所以 characters 里是会有 * 号的。


> 2. intern 机制
>
> 我在查看stringobject.c 的代码时,发现只有当字符串长度为0或者1时才会经过 intern 机制处理:
>
> PyObject *
> PyString_FromString(const char *str)
> {
> ...
> /* share short strings */
> if (size == 0) {
> PyObject *t = (PyObject *)op;
> PyString_InternInPlace(&t);
> op = (PyStringObject *)t;
> nullstring = op;
> Py_INCREF(op);
> } else if (size == 1) {
> PyObject *t = (PyObject *)op;
> PyString_InternInPlace(&t);
> op = (PyStringObject *)t;
> characters[*str & UCHAR_MAX] = op;
> Py_INCREF(op);
> }
> return (PyObject *) op;
> }
>
> 实际测试是长度超过1的字符串也会经过 intern 机制处理,不知道是否在别的地方调用的了 intern 机制?
>

PyString_FromStringAndSize 里的 intern 是自动 intern。在代码里你还可以强制 intern。
你搜 PyString_InternFromString 函数调用就能看到了。 __dict__, __name__ 等字符串也是会 intern 的。

jamie

unread,
Aug 17, 2018, 1:51:56 AM8/17/18
to python-cn(华蟒用户组,CPyUG 邮件列表)
不知道你用的 Python源码版本是多少?我用的是 Python2.5的。
下面是我修改的 string_length 函数:
static Py_ssize_t
string_length(PyStringObject *a)
{   
    char* s = PyString_AsString(a);
    if(s[0] == 'x'){
        printf("address: @%p\n", a);
        printf("refcnt: %d\n", a->ob_refcnt);
        printf("\n");
    }else if(s[0] == '?'){
        show_characters();
    }
    return a->ob_size;
}
show_characters函数打印出characters中所有的字符和引用计数值,

下面的 Python shell 的测试结果:

>>> a = '*'
>>> len('?')
...这里省略了41行(都是 value:null
value
: null  这里理论上应该输出字符'*'和其对应的 refcnt
value
: null
value
: null
value
: null
value
: null
value
: /, refcnt: 12
value
: null
value
: 1, refcnt: 2
...这里省略了许多行
1



在 2018年8月17日星期五 UTC+8下午12:57:58,Leo Jay写道:

jamie

unread,
Aug 17, 2018, 2:04:04 AM8/17/18
to python-cn(华蟒用户组,CPyUG 邮件列表)
PyString_FromStringAndSize和 PyString_FromString 代码上几乎是一样的,intern 机制不是一样的吗?你说到的“自动intern”是什么意思呢?

PyString_InternFromString的代码我之前也看了,是会强制进行 intern 处理的。


在 2018年8月17日星期五 UTC+8下午12:57:58,Leo Jay写道:
On Thu, Aug 16, 2018 at 8:51 PM jamie <ljm5...@gmail.com> wrote:

Leo Jay

unread,
Aug 17, 2018, 2:11:54 AM8/17/18
to python-cn:CPyUG
我用 2.5.6 也是得到同样的结果:
static Py_ssize_t
string_length(PyStringObject *a)
{
PyStringObject* p = characters['*'];
if (p) {
printf("DBG: Found: %s\n", PyString_AsString(p));
} else {
printf("DBG: Not Found\n");
}
return a->ob_size;
}

运行结果:
Python 2.5.6 (r256:88840, Aug 16 2018, 23:07:04)
[GCC 5.4.0 20160609] on linux4
Type "help", "copyright", "credits" or "license" for more information.
>>> a = '*'
>>> len('?')
DBG: Found: *
1
> --
> 邮件来自: `CPyUG`华蟒用户组(中文Python技术邮件列表)
> 规则: http://code.google.com/p/cpyug/wiki/PythonCn
> 详情: http://code.google.com/p/cpyug/wiki/CpyUg
> 严正: 理解列表! 智慧提问! http://wiki.woodpecker.org.cn/moin/AskForHelp
> ---
> 您收到此邮件是因为您订阅了Google网上论坛上的“python-cn(华蟒用户组,CPyUG 邮件列表)”群组。
> 要退订此群组并停止接收此群组的电子邮件,请发送电子邮件到python-cn+...@googlegroups.com
> 要发帖到此群组,请发送电子邮件至pyth...@googlegroups.com
> 要查看更多选项,请访问https://groups.google.com/d/optout

Leo Jay

unread,
Aug 17, 2018, 2:17:19 AM8/17/18
to python-cn:CPyUG
On Thu, Aug 16, 2018 at 11:04 PM jamie <ljm5...@gmail.com> wrote:
>
> PyString_FromStringAndSize和 PyString_FromString 代码上几乎是一样的,intern 机制不是一样的吗?你说到的“自动intern”是什么意思呢?

这两个函数是一样的,只有字符串长度为0或是1的时候才会 intern,其它的字符串不会 intern

>
> PyString_InternFromString的代码我之前也看了,是会强制进行 intern 处理的。

PyString_InternFromString 函数跟上面两个函数不一样。这个函数不管长度,一定会把参数 intern。
所以你上面问“实际测试是长度超过1的字符串也会经过 intern 机制处理,不知道是否在别的地方调用的了 intern 机制”
正是 PyString_InternFromString,PyString_InternImmortal 等调用了
PyString_InternInPlace 的函数使得一些长度超过1的字符串被 intern 了。

尹金都

unread,
Aug 17, 2018, 2:20:05 AM8/17/18
to pyth...@googlegroups.com
长度非1的,参考这个方法。
PyObject *
PyString_InternFromString(const char *cp)
{
PyObject *s = PyString_FromString(cp);
if (s == NULL)
return NULL;
PyString_InternInPlace(&s);
return s;
}





您收到此邮件是因为您订阅了 Google 网上论坛的“python-cn(华蟒用户组,CPyUG 邮件列表)”群组。
要退订此群组并停止接收此群组的电子邮件,请发送电子邮件到python-cn+...@googlegroups.com
要向此群组发帖,请发送电子邮件至 pyth...@googlegroups.com
要查看更多选项,请访问 https://groups.google.com/d/optout

jamie

unread,
Aug 17, 2018, 5:24:47 AM8/17/18
to python-cn(华蟒用户组,CPyUG 邮件列表)
我测试了2.5, 2.5.6两个版本,测试结果仍然没有缓存'*'。

直到我试着通过以脚本形式执行测试代码,发现结果符合预期(characters 缓存长度为1的字符串)。

不知道 Python shell 和脚本执行两者之间的区别是什么?

在 2018年8月17日星期五 UTC+8下午2:11:54,Leo Jay写道:
> 要退订此群组并停止接收此群组的电子邮件,请发送电子邮件到python-cn+unsubscribe@googlegroups.com
> 要发帖到此群组,请发送电子邮件至python-cn@googlegroups.com
> 要查看更多选项,请访问https://groups.google.com/d/optout

Shell Xu

unread,
Aug 17, 2018, 12:34:07 PM8/17/18
to CUPG
哦,忘了说了。python console里会额外多出一堆缓存来。当年摔过这个坑。。。

> 要退订此群组并停止接收此群组的电子邮件,请发送电子邮件到python-cn+...@googlegroups.com
> 要发帖到此群组,请发送电子邮件至pyth...@googlegroups.com
> 要查看更多选项,请访问https://groups.google.com/d/optout

--
邮件来自: `CPyUG`华蟒用户组(中文Python技术邮件列表)
规则: http://code.google.com/p/cpyug/wiki/PythonCn
详情: http://code.google.com/p/cpyug/wiki/CpyUg
严正: 理解列表! 智慧提问! http://wiki.woodpecker.org.cn/moin/AskForHelp
---
您收到此邮件是因为您订阅了Google网上论坛上的“python-cn(华蟒用户组,CPyUG 邮件列表)”群组。
要退订此群组并停止接收此群组的电子邮件,请发送电子邮件到python-cn+...@googlegroups.com
要发帖到此群组,请发送电子邮件至pyth...@googlegroups.com
要查看更多选项,请访问https://groups.google.com/d/optout


--
彼節者有間,而刀刃者無厚;以無厚入有間,恢恢乎其於游刃必有餘地矣。
blog: http://shell909090.org/

jaymin Lu

unread,
Aug 20, 2018, 2:26:25 AM8/20/18
to pyth...@googlegroups.com
请问“缓存”具体指的是什么?

> 要退订此群组并停止接收此群组的电子邮件,请发送电子邮件到python-cn+unsubscribe@googlegroups.com
> 要发帖到此群组,请发送电子邮件至python-cn@googlegroups.com
> 要查看更多选项,请访问https://groups.google.com/d/optout

--
邮件来自: `CPyUG`华蟒用户组(中文Python技术邮件列表)
规则: http://code.google.com/p/cpyug/wiki/PythonCn
详情: http://code.google.com/p/cpyug/wiki/CpyUg
严正: 理解列表! 智慧提问! http://wiki.woodpecker.org.cn/moin/AskForHelp
---
您收到此邮件是因为您订阅了Google网上论坛上的“python-cn(华蟒用户组,CPyUG 邮件列表)”群组。
要退订此群组并停止接收此群组的电子邮件,请发送电子邮件到python-cn+unsubscribe@googlegroups.com
要发帖到此群组,请发送电子邮件至python-cn@googlegroups.com
要查看更多选项,请访问https://groups.google.com/d/optout


--
彼節者有間,而刀刃者無厚;以無厚入有間,恢恢乎其於游刃必有餘地矣。
blog: http://shell909090.org/

--
邮件来自: `CPyUG`华蟒用户组(中文Python技术邮件列表)
规则: http://code.google.com/p/cpyug/wiki/PythonCn
详情: http://code.google.com/p/cpyug/wiki/CpyUg
严正: 理解列表! 智慧提问! http://wiki.woodpecker.org.cn/moin/AskForHelp
---
您收到此邮件是因为您订阅了Google网上论坛上的“python-cn(华蟒用户组,CPyUG 邮件列表)”群组。

jaymin Lu

unread,
Aug 20, 2018, 2:30:41 AM8/20/18
to pyth...@googlegroups.com
我猜会不会是系统环境或者编译参数的影响,导致我们编译出来的 Python “不太一致”,可以麻烦你提供一下你的编译参数吗?

> 要退订此群组并停止接收此群组的电子邮件,请发送电子邮件到python-cn+unsubscribe@googlegroups.com
> 要发帖到此群组,请发送电子邮件至python-cn@googlegroups.com
> 要查看更多选项,请访问https://groups.google.com/d/optout

--
邮件来自: `CPyUG`华蟒用户组(中文Python技术邮件列表)
规则: http://code.google.com/p/cpyug/wiki/PythonCn
详情: http://code.google.com/p/cpyug/wiki/CpyUg
严正: 理解列表! 智慧提问! http://wiki.woodpecker.org.cn/moin/AskForHelp
---
您收到此邮件是因为您订阅了 Google 网上论坛的“python-cn(华蟒用户组,CPyUG 邮件列表)”群组。
要退订此群组并停止接收此群组的电子邮件,请发送电子邮件到python-cn+unsubscribe@googlegroups.com

Leo Jay

unread,
Aug 20, 2018, 2:35:41 AM8/20/18
to python-cn:CPyUG
我的环境: Ubuntu 16.04.5
编译方式:
./configure --prefix=$HOME/test/python-test
make && make install
>> > 要退订此群组并停止接收此群组的电子邮件,请发送电子邮件到python-cn+...@googlegroups.com
>> > 要发帖到此群组,请发送电子邮件至pyth...@googlegroups.com
>> > 要查看更多选项,请访问https://groups.google.com/d/optout
>>
>> --
>> 邮件来自: `CPyUG`华蟒用户组(中文Python技术邮件列表)
>> 规则: http://code.google.com/p/cpyug/wiki/PythonCn
>> 详情: http://code.google.com/p/cpyug/wiki/CpyUg
>> 严正: 理解列表! 智慧提问! http://wiki.woodpecker.org.cn/moin/AskForHelp
>> ---
>> 您收到此邮件是因为您订阅了 Google 网上论坛的“python-cn(华蟒用户组,CPyUG 邮件列表)”群组。
>> 要退订此群组并停止接收此群组的电子邮件,请发送电子邮件到python-cn+...@googlegroups.com
>> 要向此群组发帖,请发送电子邮件至 pyth...@googlegroups.com
>> 要查看更多选项,请访问 https://groups.google.com/d/optout
>
>
> --
> 邮件来自: `CPyUG`华蟒用户组(中文Python技术邮件列表)
> 规则: http://code.google.com/p/cpyug/wiki/PythonCn
> 详情: http://code.google.com/p/cpyug/wiki/CpyUg
> 严正: 理解列表! 智慧提问! http://wiki.woodpecker.org.cn/moin/AskForHelp
> ---
> 您收到此邮件是因为您订阅了Google网上论坛上的“python-cn(华蟒用户组,CPyUG 邮件列表)”群组。

jaymin Lu

unread,
Aug 20, 2018, 2:50:45 AM8/20/18
to pyth...@googlegroups.com
我的编译参数和你的一样,不过我是 docker环境(Ubuntu 18.04.1 LTS)下编译的。我等下试试 Ubuntu 16.04.5,不过感觉结果还是一样的。

>> > 要退订此群组并停止接收此群组的电子邮件,请发送电子邮件到python-cn+unsubscribe@googlegroups.com
>> > 要发帖到此群组,请发送电子邮件至python-cn@googlegroups.com

>> > 要查看更多选项,请访问https://groups.google.com/d/optout
>>
>> --
>> 邮件来自: `CPyUG`华蟒用户组(中文Python技术邮件列表)
>> 规则: http://code.google.com/p/cpyug/wiki/PythonCn
>> 详情: http://code.google.com/p/cpyug/wiki/CpyUg
>> 严正: 理解列表! 智慧提问! http://wiki.woodpecker.org.cn/moin/AskForHelp
>> ---
>> 您收到此邮件是因为您订阅了 Google 网上论坛的“python-cn(华蟒用户组,CPyUG 邮件列表)”群组。
>> 要退订此群组并停止接收此群组的电子邮件,请发送电子邮件到python-cn+unsubscribe@googlegroups.com

>> 要向此群组发帖,请发送电子邮件至 pyth...@googlegroups.com
>> 要查看更多选项,请访问 https://groups.google.com/d/optout
>
>
> --
> 邮件来自: `CPyUG`华蟒用户组(中文Python技术邮件列表)
> 规则: http://code.google.com/p/cpyug/wiki/PythonCn
> 详情: http://code.google.com/p/cpyug/wiki/CpyUg
> 严正: 理解列表! 智慧提问! http://wiki.woodpecker.org.cn/moin/AskForHelp
> ---
> 您收到此邮件是因为您订阅了Google网上论坛上的“python-cn(华蟒用户组,CPyUG 邮件列表)”群组。
> 要退订此群组并停止接收此群组的电子邮件,请发送电子邮件到python-cn+unsubscribe@googlegroups.com
> 要发帖到此群组,请发送电子邮件至python-cn@googlegroups.com

> 要查看更多选项,请访问https://groups.google.com/d/optout

--
邮件来自: `CPyUG`华蟒用户组(中文Python技术邮件列表)
规则: http://code.google.com/p/cpyug/wiki/PythonCn
详情: http://code.google.com/p/cpyug/wiki/CpyUg
严正: 理解列表! 智慧提问! http://wiki.woodpecker.org.cn/moin/AskForHelp
---
您收到此邮件是因为您订阅了 Google 网上论坛的“python-cn(华蟒用户组,CPyUG 邮件列表)”群组。
要退订此群组并停止接收此群组的电子邮件,请发送电子邮件到python-cn+unsubscribe@googlegroups.com

jaymin Lu

unread,
Aug 20, 2018, 3:25:26 AM8/20/18
to pyth...@googlegroups.com
结果还是一样:Python shell 不符合预期,Python 执行脚本符合预期。

Shell Xu

unread,
Aug 20, 2018, 9:43:18 AM8/20/18
to CUPG
我也不知道是什么。从结果来看,很多在脚本里ID不一致的对象会出现ID一致的现象。

请问“缓存”具体指的是什么?

> 要退订此群组并停止接收此群组的电子邮件,请发送电子邮件到python-cn+...@googlegroups.com
> 要发帖到此群组,请发送电子邮件至pyth...@googlegroups.com
> 要查看更多选项,请访问https://groups.google.com/d/optout

--
邮件来自: `CPyUG`华蟒用户组(中文Python技术邮件列表)
规则: http://code.google.com/p/cpyug/wiki/PythonCn
详情: http://code.google.com/p/cpyug/wiki/CpyUg
严正: 理解列表! 智慧提问! http://wiki.woodpecker.org.cn/moin/AskForHelp
---
您收到此邮件是因为您订阅了Google网上论坛上的“python-cn(华蟒用户组,CPyUG 邮件列表)”群组。
要退订此群组并停止接收此群组的电子邮件,请发送电子邮件到python-cn+...@googlegroups.com
要发帖到此群组,请发送电子邮件至pyth...@googlegroups.com
要查看更多选项,请访问https://groups.google.com/d/optout


--
彼節者有間,而刀刃者無厚;以無厚入有間,恢恢乎其於游刃必有餘地矣。
blog: http://shell909090.org/

--
邮件来自: `CPyUG`华蟒用户组(中文Python技术邮件列表)
规则: http://code.google.com/p/cpyug/wiki/PythonCn
详情: http://code.google.com/p/cpyug/wiki/CpyUg
严正: 理解列表! 智慧提问! http://wiki.woodpecker.org.cn/moin/AskForHelp
---
您收到此邮件是因为您订阅了Google网上论坛上的“python-cn(华蟒用户组,CPyUG 邮件列表)”群组。
要退订此群组并停止接收此群组的电子邮件,请发送电子邮件到python-cn+...@googlegroups.com
要发帖到此群组,请发送电子邮件至pyth...@googlegroups.com
要查看更多选项,请访问https://groups.google.com/d/optout

--
邮件来自: `CPyUG`华蟒用户组(中文Python技术邮件列表)
规则: http://code.google.com/p/cpyug/wiki/PythonCn
详情: http://code.google.com/p/cpyug/wiki/CpyUg
严正: 理解列表! 智慧提问! http://wiki.woodpecker.org.cn/moin/AskForHelp
---
您收到此邮件是因为您订阅了Google网上论坛上的“python-cn(华蟒用户组,CPyUG 邮件列表)”群组。
要退订此群组并停止接收此群组的电子邮件,请发送电子邮件到python-cn+...@googlegroups.com
要发帖到此群组,请发送电子邮件至pyth...@googlegroups.com
要查看更多选项,请访问https://groups.google.com/d/optout
Reply all
Reply to author
Forward
0 new messages