python - لغة - مكتبة اكواد بايثون



لماذا يتغير حجم سلسلة Python هذه عند تحويل int فاشل (1)

من سقسقة هنا :

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

نحصل على 74 ، ثم 77 بايت للمكالمات getsizeof اثنين.

يبدو أننا نقوم بإضافة 3 بايت إلى الكائن ، من استدعاء int الفاشلة.

بعض الأمثلة الأخرى من twitter (قد تحتاج إلى إعادة تشغيل python لإعادة ضبط الحجم إلى 74):

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

77!

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

74 ، ثم 77.


تطلب التعليمة البرمجية التي تحول السلاسل إلى ints في CPython 3.6 شكل 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);
}

البايتات الثلاثة الإضافية هي لتمثيل 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))

قد تتساءل أيضًا لماذا لا يتغير الحجم لشيء مثل '1' . هذا هو U + FF11 FULLWIDTH DIGIT ONE ، والذي يعامل int بما يعادل '1' . ذلك لأن واحدة من الخطوات السابقة في عملية السلسلة إلى int هي

asciidig = _PyUnicode_TransformDecimalAndSpaceToASCII(u);

الذي يحول جميع أحرف المسافة البيضاء إلى ' ' ويحول كل الأرقام العشرية في Unicode إلى أرقام ASCII المقابلة. يُرجع هذا التحويل السلسلة الأصلية إذا لم ينتهي تغيير أي شيء ، ولكن عندما يحدث تغييرات ، فإنه ينشئ سلسلة جديدة ، والسلسلة الجديدة هي السلسلة التي يتم فيها إنشاء تمثيل UTF-8.

بالنسبة للحالات التي يبدو فيها الاتصال int على سلسلة ما يؤثر على أخرى ، فهذه هي في الواقع نفس كائن السلسلة. هناك العديد من الشروط التي بموجبها ستقوم Python بإعادة استخدام السلاسل ، كل هذا بنفس القدر من الحزم في Weird Implementation Detail Land مثل كل شيء ناقشناه حتى الآن. بالنسبة 'ñ' ، يحدث إعادة الاستخدام لأن هذا عبارة عن سلسلة أحرف واحدة في نطاق Latin-1 ( '\x00' - '\xff' ) ، ويقوم التطبيق بتخزينها وإعادة '\xff' .





python-internals