V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
xinali
V2EX  ›  Python

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

  •  
  •   xinali · 2016-08-02 13:27:44 +08:00 · 2922 次点击
    这是一个创建于 2835 天前的主题,其中的信息可能已经有所发展或是发生改变。
    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(字节数)是怎么算的呢?

    16 条回复    2016-08-02 18:06:50 +08:00
    pixstone
        1
    pixstone  
       2016-08-02 13:37:22 +08:00
    len 是是返回 char 的数量
    sizeof 是返回占用空间,比如 utf - 8 编码下的 占用空间多, 假如 "a" 在 ascii 中占一位,那么 “续” 在 unicode 就占 2 位了, 但 len 都是 1 。具体 utf - 8 和 ascii 具体占几位 我忘记了。。
    xinali
        2
    xinali  
    OP
       2016-08-02 14:00:46 +08:00
    @pixstone 你说的我知道,我不清楚的是,怎么计算的 sizeof ,字节数我怎么算也跟它给出的不一样
    GeekGao
        3
    GeekGao  
       2016-08-02 14:25:36 +08:00
    __sizeof__是 2.6+版本支持的,但不作为生产环境用途, 与之相关功能说明请参考文档中的: sys.getsizeof ,它不是用来计算 string 长度的!
    justou
        4
    justou  
       2016-08-02 14:42:05 +08:00   ❤️ 2
    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
        5
    xinali  
    OP
       2016-08-02 14:49:23 +08:00
    @GeekGao 您能先把问题看好了,再回答吗?
    justou
        6
    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
        7
    GeekGao  
       2016-08-02 14:52:19 +08:00
    @xinali 你没看懂我意思么,意思就是说你这么算 unicode 长度是不对的, len 和__sizeof__ 不是一个概念。
    ovear
        8
    ovear  
       2016-08-02 14:53:55 +08:00
    https://zh.wikipedia.org/zh/UTF-8 utf8 是变长的。。
    GeekGao
        9
    GeekGao  
       2016-08-02 14:54:08 +08:00
    @justou 官方邮件组说不同平台返回结果可能不同
    xinali
        10
    xinali  
    OP
       2016-08-02 14:55:53 +08:00
    @GeekGao 我的意思它俩就不是一个概念,所以才想弄清楚 sizeof 的计算方式的
    xinali
        11
    xinali  
    OP
       2016-08-02 15:01:23 +08:00
    @ovear 这个还没有讨论到具体的字符集的问题
    mulog
        12
    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
        13
    yangtukun1412  
       2016-08-02 15:27:03 +08:00   ❤️ 1
    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
        14
    GeekGao  
       2016-08-02 15:35:59 +08:00   ❤️ 1
    看了下代码,关于这个__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
        15
    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
        16
    shuax  
       2016-08-02 18:06:50 +08:00
    sizeof 的意思是我实现这个东西的时候花了多少内存,和 len 没有什么好比较的。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   1072 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 35ms · UTC 22:21 · PVG 06:21 · LAX 15:21 · JFK 18:21
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.