web-dev-qa-db-ja.com

なぜこのPython失敗したint変換で文字列が変更されるのか

Tweet here から:

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

2つのgetsizeof呼び出しに対して74バイト、次に77バイトを取得します。

失敗したint呼び出しから、オブジェクトに3バイトを追加しているようです。

Twitterのその他の例(サイズを74にリセットするには、pythonを再起動する必要があります):

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

77!

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

74、次に77。

71
jeremycg

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 FULLWIDTH DIGIT ONEで、int'1'と同等として処理します。これは、 string-to-intプロセスの以前のステップの1つ

asciidig = _PyUnicode_TransformDecimalAndSpaceToASCII(u);

すべての空白文字を' 'に変換し、すべてのUnicode 10進数字を対応するASCII数字に変換します。最終的に何も変更しない場合、この変換は元の文字列を返しますが、変更を行うと新しい文字列を作成し、新しい文字列はUTF-8表現を作成するものになります。


ある文字列でintを呼び出すと、別の文字列に影響するように見える場合は、実際には同じ文字列オブジェクトです。 Pythonが文字列を再利用する多くの条件があります。これは、これまで説明してきたすべてと同じように、Weird Implementation Detail Landでも同じです。 'ñ'の場合、これはLatin-1範囲('\x00'-'\xff')の1文字の文字列であり、実装 ストアとそれらを再利用します

71
user2357112