python 中 str 和 unicode 存储所占字节数目问题

2016-08-02 13:27:44 +08:00
 xinali
In [243]: s1 = '\xe4\xb9\xa0\xe6\x83\xaf'

In [244]: s2 = u'\xe4\xb9\xa0\xe6\x83\xaf'

In [245]: len(s1)
Out[245]: 6

In [246]: len(s2)
Out[246]: 6

In [247]: s1.__sizeof__()
Out[247]: 43

In [248]: s2.__sizeof__()
Out[248]: 76

s1 的数据类型为 str , s2 的数据类型为 unicode ,其中两个数据的 len 是相同的,但是其所占用的 bytes(字节数)是怎么算的呢?

3007 次点击
所在节点    Python
16 条回复
pixstone
2016-08-02 13:37:22 +08:00
len 是是返回 char 的数量
sizeof 是返回占用空间,比如 utf - 8 编码下的 占用空间多, 假如 "a" 在 ascii 中占一位,那么 “续” 在 unicode 就占 2 位了, 但 len 都是 1 。具体 utf - 8 和 ascii 具体占几位 我忘记了。。
xinali
2016-08-02 14:00:46 +08:00
@pixstone 你说的我知道,我不清楚的是,怎么计算的 sizeof ,字节数我怎么算也跟它给出的不一样
GeekGao
2016-08-02 14:25:36 +08:00
__sizeof__是 2.6+版本支持的,但不作为生产环境用途, 与之相关功能说明请参考文档中的: sys.getsizeof ,它不是用来计算 string 长度的!
justou
2016-08-02 14:42:05 +08:00
sizeof 计算的是那个对象占用的空间(跟 C 语言的 sizeof 类似), 而 str, unicode 在底层都是一个 C 结构体表示的, 所以这样算出来的字节数是整个结构体的字节数(包括里面字符指针的内存大小)

如果要获得里面字符字节数, 需要直接访问里面的 buffer 信息
from array import array

s1 = '\xe4\xb9\xa0\xe6\x83\xaf'
s2 = u'\xe4\xb9\xa0\xe6\x83\xaf'

arr = array('c', s1)
print arr.buffer_info()[1] * arr.itemsize

uarr = array('u', s2)
print uarr.buffer_info()[1] * uarr.itemsize
xinali
2016-08-02 14:49:23 +08:00
@GeekGao 您能先把问题看好了,再回答吗?
justou
2016-08-02 14:50:57 +08:00
另外, 也可以这样来测试
In [1]: ''.__sizeof__()
Out[1]: 21

In [2]: '1'.__sizeof__()
Out[2]: 22

In [3]: '123'.__sizeof__()
Out[3]: 24

In [4]: u''.__sizeof__()
Out[4]: 26

In [5]: u'1'.__sizeof__()
Out[5]: 28

创建一个空对象来看这个对象对应结构体的大小, 然后慢慢加字符进去看大小之差
GeekGao
2016-08-02 14:52:19 +08:00
@xinali 你没看懂我意思么,意思就是说你这么算 unicode 长度是不对的, len 和__sizeof__ 不是一个概念。
ovear
2016-08-02 14:53:55 +08:00
https://zh.wikipedia.org/zh/UTF-8 utf8 是变长的。。
GeekGao
2016-08-02 14:54:08 +08:00
@justou 官方邮件组说不同平台返回结果可能不同
xinali
2016-08-02 14:55:53 +08:00
@GeekGao 我的意思它俩就不是一个概念,所以才想弄清楚 sizeof 的计算方式的
xinali
2016-08-02 15:01:23 +08:00
@ovear 这个还没有讨论到具体的字符集的问题
mulog
2016-08-02 15:23:32 +08:00
python 的 str 又不是只存字符串本身。。

```C
typedef struct {
PyObject_VAR_HEAD
long ob_shash;
int ob_sstate;
char ob_sval[1];
} PyStringObject;
```
具体怎么算我也不知道,我猜是 16 + 8 + 4 + 8 + len(your_str) + 1
unicode 同理,有兴趣去看代码嘛。。
yangtukun1412
2016-08-02 15:27:03 +08:00
64 位默认情况下, PyStringObject 结构体占用 37 字节, 每增加一个字符就多一个 char 的 size.
PyUnicodeObject 结构体占用 48 字节, 每增加一个字符就多一个 Py_UNICODE_TYPE 的 size, 这个值是在编译时根据 Py_UNICODE_SIZE 指定的, 默认 ucs2 的情况下应该是 unsigned short, 2 字节.

值得注意的是, unicode 在计算 size 的时候多加了 1 位: sizeof(Py_UNICODE) * (v->length + 1), 因此 sys.getsizeof(u'') == 50.
GeekGao
2016-08-02 15:35:59 +08:00
看了下代码,关于这个__sizeof__方法不同对象提供的方法不同,其中你要的 stringobject 的实现:

static PyObject *
string_sizeof(PyStringObject *v)
{
Py_ssize_t res;
res = PyStringObject_SIZE + v->ob_size * v->ob_type->tp_itemsize;
return PyInt_FromSsize_t(res);
}

其中 v->ob_size 是实际长度( byte ),
PyStringObject_SIZE 相当于 sizeof(PyStringObject),
tp_itemsize 为 sizeof(char)

代码版本: CPython 2.7.x
GeekGao
2016-08-02 16:06:16 +08:00
unicodeobject 对应实现为:
static PyObject *
unicode__sizeof__(PyUnicodeObject *v)
{
return PyInt_FromSsize_t(sizeof(PyUnicodeObject) +
sizeof(Py_UNICODE) * (v->length + 1));
}
shuax
2016-08-02 18:06:50 +08:00
sizeof 的意思是我实现这个东西的时候花了多少内存,和 len 没有什么好比较的。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/296549

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX