为什么此 Python 字符串的大小会在 int 转换失败时发生变化

Why does the size of this Python String change on a failed int conversion

来自tweet here

import sys
x = 'ñ'
print(sys.getsizeof(x))
int(x) #throws an error
print(sys.getsizeof(x))

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

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

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

x = 'ñ'
y = 'ñ'
int(x)
print(sys.getsizeof(y))

77!

print(sys.getsizeof('ñ'))
int('ñ')
print(sys.getsizeof('ñ'))

74,然后是 77。

C中字符串转int的代码Python 3.6 requests a UTF-8 form of the string to work with:

buffer = PyUnicode_AsUTF8AndSize(asciidig, &buflen);

并且该字符串在第一次被请求时创建 UTF-8 表示并且 caches it on the string object:

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);
}

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


您可能想知道为什么当字符串类似于 '40''plain ascii text' 时大小不会改变。那是因为如果字符串在 "compact ascii" representation, Python doesn't create a separate UTF-8 representation. It returns the ASCII representation directly 中,它已经是有效的 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))

您可能还想知道为什么像 '1' 这样的大小没有改变。那是 U+FF11 FULLWIDTH DIGIT ONE,int 将其视为等同于 '1'。那是因为string-to-int过程中的one of the earlier steps

asciidig = _PyUnicode_TransformDecimalAndSpaceToASCII(u);

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


至于在一个字符串上调用 int 看起来影响另一个字符串的情况,它们实际上是同一个字符串对象。在很多情况下 Python 将重用字符串,所有这些都与我们目前讨论的所有内容一样坚定地存在于 Weird Implementation Detail Land 中。对于 'ñ',重用发生是因为这是 Latin-1 范围 ('\x00'-'\xff') 中的单字符字符串,并且实现 stores and reuses those.