Pourquoi la taille de cette chaîne Python change-t-elle lors d'une conversion int échouée

De la tweet ici:

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

Nous obtenons 74, puis 77 octets pour les deux getsizeof appels.

Il semble que nous ajoutions 3 octets à l'objet, à partir de l'appel int échoué.

Quelques autres exemples de twitter (vous devrez peut-être redémarrer python pour réinitialiser la taille à 74):

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

77!

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

74, puis 77.

70
demandé sur Jim Fasarakis Hilliard 2017-11-01 22:21:49

1 réponses

Le code qui convertit les chaînes en ints dans CPython 3.6 demande une forme UTF-8 de la chaîne pour fonctionner avec :

buffer = PyUnicode_AsUTF8AndSize(asciidig, &buflen);

Et la chaîne crée la représentation UTF-8 la première fois qu'elle est demandée et La met en cache sur l'objet string :

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

Les 3 octets supplémentaires sont pour la représentation UTF-8.


, Vous demandez peut-être pourquoi la taille ne change pas lorsque la chaîne est quelque chose comme '40' ou 'plain ascii text'. C'est parce que si la chaîne est en représentation "ASCII compacte" , Python ne crée pas de représentation UTF-8 séparée. Il renvoie directement la représentation ASCII , qui est déjà valide 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))

Vous pouvez également vous demander pourquoi la taille ne change pas pour quelque chose comme '1'. C'est U + FF11 fullwidth DIGIT ONE, QUE int traite comme équivalent à '1'. C'est parce que l'une des étapes antérieures dans la chaîne-de-int processus est

asciidig = _PyUnicode_TransformDecimalAndSpaceToASCII(u);

Qui convertit tout les caractères d'espace en ' ' et convertit tous les chiffres décimaux Unicode en chiffres ASCII correspondants. Cette conversion renvoie la chaîne d'origine si elle ne finit pas par changer quoi que ce soit, mais quand elle fait des modifications, elle crée une nouvelle chaîne, et la nouvelle chaîne est celle qui obtient une représentation UTF-8 créée.


En ce qui concerne les cas où l'appel de int sur une chaîne semble affecter une autre, ce sont en fait le même objet string. Il y a beaucoup de conditions quel Python réutilisera les chaînes, tout aussi fermement dans la Terre de détail D'implémentation étrange que tout ce que nous avons discuté jusqu'à présent. Pour 'ñ', la réutilisation se produit car il s'agit d'une chaîne à un seul caractère dans la plage Latin-1('\x00'-'\xff'), et l'implémentation stocke et réutilise ceux-ci .

71
répondu user2357112 2017-11-02 00:14:34