【问题标题】:Why does the size of this Python String change on a failed int conversion为什么这个 Python 字符串的大小在 int 转换失败时会发生变化
【发布时间】:2018-04-14 04:52:26
【问题描述】:

来自tweet here

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

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

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

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

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

77!

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

74,然后是 77。

【问题讨论】:

  • 必须和PEP393相关的灵活字符串表示。
  • 在点击推文链接之前我知道是比兹利发了推文。
  • 是的,看来我得攒钱去他的一门课程了

标签: python string python-3.x unicode python-internals


【解决方案1】:

CPython 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 不会创建单独的 UTF-8 表示。它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'。那是因为one of the earlier steps在string-to-int过程中是

asciidig = _PyUnicode_TransformDecimalAndSpaceToASCII(u);

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


至于在一个字符串上调用int 看起来会影响另一个字符串的情况,它们实际上是同一个字符串对象。 Python 将在许多条件下重用字符串,所有这些都在 Weird Implementation Detail Land 中与我们迄今为止讨论的所有内容一样牢固。对于'ñ',会发生重用,因为这是拉丁1 范围内的单字符字符串('\x00'-'\xff'),以及实现stores and reuses those

【讨论】:

  • @jeremycg:您的 sn-p 从不请求 UTF-8。字符串连接不执行 UTF-8 转换。
  • 这是一项了不起的工作,但对我来说有点难以理解为什么 int('ñ') 产生的错误产生 3 的进位,而不是 int('[') 产生的错误例子。有什么区别?
  • 好吧,我猜我们还在内存中保存了 'ñ',然后修改它,而不是一个副本,还解释了 x/y 和 'ñ','ñ' 一个.我想就是这样,但会推迟接受,看看是否还有其他想法。
  • @DamianLattenero:我的回答现在解决了这个问题。
  • 谢谢回复,我明白了!很好的答案
猜你喜欢
  • 2015-01-18
  • 1970-01-01
  • 1970-01-01
  • 2018-10-04
  • 2012-05-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-05-24
相关资源
最近更新 更多