tipos - python separar string por caracter
¿Por qué cambia el tamaño de esta cadena de Python en una conversión int fallida? (1)
El código que convierte cadenas en ints en CPython 3.6 solicita una forma UTF-8 de la cadena para trabajar con :
buffer = PyUnicode_AsUTF8AndSize(asciidig, &buflen);
y la cadena crea la representación UTF-8 la primera vez que se solicita y la almacena en caché en el objeto de cadena :
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);
}
Los 3 bytes adicionales son para la representación UTF-8.
Tal vez se pregunte por qué el tamaño no cambia cuando la cadena es algo así como
''40''
o
''plain ascii text''
.
Esto se debe a que si la cadena está en
representación "ascii compacta"
, Python no crea una representación UTF-8 separada.
Devuelve la representación ASCII directamente
, que ya es válida 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))
También puede preguntarse por qué el tamaño no cambia para algo como
''1''
.
Eso es U + FF11 FULLWIDTH DIGIT ONE, que
int
trata como equivalente a
''1''
.
Eso es porque
uno de los pasos anteriores
en el proceso de cadena a int es
asciidig = _PyUnicode_TransformDecimalAndSpaceToASCII(u);
que convierte todos los caracteres de espacio en blanco en
'' ''
y convierte todos los dígitos decimales Unicode en los dígitos ASCII correspondientes.
Esta conversión devuelve la cadena original si no termina cambiando nada, pero cuando realiza cambios, crea una nueva cadena, y la nueva cadena es la que obtiene una representación UTF-8 creada.
En cuanto a los casos en que llamar a
int
en una cadena parece afectar a otra, en realidad son el mismo objeto de cadena.
Hay muchas condiciones bajo las cuales Python reutilizará cadenas, todas tan firmemente en Weird Implementation Detail Land como todo lo que hemos discutido hasta ahora.
Para
''ñ''
, la reutilización ocurre porque esta es una cadena de un solo carácter en el rango de Latin-1 (
''/x00''
-
''/xff''
), y la implementación
almacena y reutiliza esos
.
Del tweet aquí :
import sys
x = ''ñ''
print(sys.getsizeof(x))
int(x) #throws an error
print(sys.getsizeof(x))
Obtenemos 74, luego 77 bytes para las dos llamadas de
getsizeof
.
Parece que estamos agregando 3 bytes al objeto, desde la llamada int fallida.
Algunos ejemplos más de twitter (es posible que deba reiniciar Python para restablecer el tamaño a 74):
x = ''ñ''
y = ''ñ''
int(x)
print(sys.getsizeof(y))
77!
print(sys.getsizeof(''ñ''))
int(''ñ'')
print(sys.getsizeof(''ñ''))
74, luego 77.