为什么这个Python String的大小会在失败的int转换中发生变化

jer*_*ycg 71 python string unicode python-3.x python-internals

这里推文:

import sys
x = 'ñ'
print(sys.getsizeof(x))
int(x) #throws an error
print(sys.getsizeof(x))
Run Code Online (Sandbox Code Playgroud)

我们得到74,然后是两个getsizeof调用的77个字节.

看起来我们正在从失败的int调用中向对象添加3个字节.

来自twitter的更多示例(您可能需要重新启动python以将大小重置为74):

x = 'ñ'
y = 'ñ'
int(x)
print(sys.getsizeof(y))
Run Code Online (Sandbox Code Playgroud)

77!

print(sys.getsizeof('ñ'))
int('ñ')
print(sys.getsizeof('ñ'))
Run Code Online (Sandbox Code Playgroud)

74,然后77.

use*_*ica 71

在CPython 3.6中将字符串转换为int的代码请求使用字符串的UTF-8形式:

buffer = PyUnicode_AsUTF8AndSize(asciidig, &buflen);
Run Code Online (Sandbox Code Playgroud)

并且字符串在第一次请求时创建UTF-8表示并将其缓存在字符串对象上:

if (PyUnicode_UTF8(unicode) == NULL) {
    assert(!PyUnicode_IS_COMPACT_ASCII(unicode));
    bytes = _PyUnicode_AsUTF8String(unicode, NULL);
    if (bytes == NULL)
        return NULL;
    _PyUnicode_UTF8(unicode) = PyObject_MALLOC(PyBytes_GET_SIZE(bytes) + 1);
    if (_PyUnicode_UTF8(unicode) == NULL) {
        PyErr_NoMemory();
        Py_DECREF(bytes);
        return NULL;
    }
    _PyUnicode_UTF8_LENGTH(unicode) = PyBytes_GET_SIZE(bytes);
    memcpy(_PyUnicode_UTF8(unicode),
              PyBytes_AS_STRING(bytes),
              _PyUnicode_UTF8_LENGTH(unicode) + 1);
    Py_DECREF(bytes);
}
Run Code Online (Sandbox Code Playgroud)

额外的3个字节用于UTF-8表示.


您可能想知道为什么当字符串类似于'40'或时,大小不会改变'plain ascii text'.那是因为如果字符串是"紧凑的ascii"表示,Python不会创建单独的UTF-8表示.它直接返回ASCII表示,它已经是有效的UTF-8:

#define PyUnicode_UTF8(op)                              \
    (assert(_PyUnicode_CHECK(op)),                      \
     assert(PyUnicode_IS_READY(op)),                    \
     PyUnicode_IS_COMPACT_ASCII(op) ?                   \
         ((char*)((PyASCIIObject*)(op) + 1)) :          \
         _PyUnicode_UTF8(op))
Run Code Online (Sandbox Code Playgroud)

您也可能想知道为什么尺寸不会因为类似的东西而改变'?'.这是U + FF11 FULLWIDTH DIGIT ONE,它int相当于'1'.那是因为string-to-int过程中的早期步骤之一是

asciidig = _PyUnicode_TransformDecimalAndSpaceToASCII(u);
Run Code Online (Sandbox Code Playgroud)

它将所有空白字符' '转换为并将所有Unicode十进制数字转换为相应的ASCII数字.如果它不会更改任何内容,则此转换将返回原始字符串,但是当它确实进行更改时,它会创建一个新字符串,并且新字符串是创建UTF-8表示的字符串.


至于调用int一个字符串看起来影响另一个字符串的情况,那些实际上是相同的字符串对象.在许多条件下,Python将重用字符串,就像我们到目前为止讨论的所有内容一样,在Weird Implementation Detail Land中也是如此.因为'ñ',重复发生是因为这是Latin-1范围('\x00'- '\xff')中的单字符字符串,并且实现存储并重用这些字符串.