为什么在失败的int转换中此Python字符串的大小会更改


70

这里推文中

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

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

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

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

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

77!

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

74,然后77。


必须与PEP393灵活字符串表示形式相关。
VPfB

14
甚至在不点击tweet链接之前,我就知道是Beazley发了推文。
Dimitris Fasarakis Hilliard '11

是的,我似乎越来越有可能不得不积蓄去上他的一门课程
jeremycg

Answers:


70

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

buffer = PyUnicode_AsUTF8AndSize(asciidig, &buflen);

并且该字符串在首次请求时创建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);
}

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


您可能想知道,当字符串为'40'或时,为什么大小不改变'plain ascii text'。这是因为,如果字符串使用“ compact 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))

您可能还会想知道,为什么尺寸不会像一样改变'1'。这就是U + FF11全长数字ONE,它int等同于'1'。这是因为string-to-int程序中较早的步骤之一是

asciidig = _PyUnicode_TransformDecimalAndSpaceToASCII(u);

它将所有空格字符' '转换为并将所有Unicode十进制数字转换为相应的ASCII数字。如果此转换没有结束任何更改,则返回原始字符串,但是当进行更改时,它将创建一个新字符串,并且该新字符串是将创建UTF-8表示形式的字符串。


至于调用int一个字符串看起来会影响另一个字符串的情况,则实际上是相同的字符串对象。在许多情况下,Python将重用字符串,正如到目前为止我们所讨论的一切一样,在Weird Implementation Detail领域中,这些条件都同样牢固。对于'ñ',发生重用是因为这是Latin-1范围('\x00'- '\xff')中的单字符字符串,并且实现存储并重用了这些字符。


@jeremycg:您的代码段从不请求UTF-8。字符串串联不执行UTF-8转换。
user2357112支持Monica17年

1
那是很棒的工作,但对我而言,很难理解为什么int('ñ')产生的错误产生的进位为3,而不是例如int('[')的错误。有什么区别?
的Damián拉斐尔Lattenero

好的,我想我们也将'ñ'保留在内存中,并对其进行修改,而不是对其进行复制,同时解释了x / y和'ñ','ñ'一个。我认为就是这样,但是会推迟接受调查,看看是否还有其他想法。
jeremycg

1
@DamianLattenero:我的回答现在解决了。
user2357112支持Monica's

感谢您的答复,现在我明白了!伟大的答案
的Damián拉斐尔Lattenero
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.