python - ¿Por qué un espacio afecta la comparación de identidad de cadenas iguales?
(1)
El intérprete de Python almacena en caché algunas cadenas en función de ciertos criterios, la primera cadena
abc
se almacena en caché y se usa para ambos, pero la segunda no.
Es lo mismo para pequeñas entradas de
-5
a
256
.
Debido a que las cadenas están internadas / en caché, la asignación de
a
y
b
a
"abc"
hace que
a
y
b
apunten a los mismos objetos en la memoria, por lo que usar
is
, que comprueba si dos objetos son realmente el mismo objeto, devuelve
True
.
La segunda cadena
abc abc
no se almacena en la memoria caché, por lo que son dos objetos completamente diferentes en la memoria, por lo que la verificación de identidad que se usa
is
False
.
Esta vez
a
no
es
b
.
Ambos apuntan a diferentes objetos en la memoria.
In [43]: a = "abc" # python caches abc
In [44]: b = "abc" # it reuses the object when assigning to b
In [45]: id(a)
Out[45]: 139806825858808 # same id''s, same object in memory
In [46]: id(b)
Out[46]: 139806825858808
In [47]: a = ''abc abc'' # not cached
In [48]: id(a)
Out[48]: 139806688800984
In [49]: b = ''abc abc''
In [50]: id(b) # different id''s different objects
Out[50]: 139806688801208
El criterio para el almacenamiento en caché de cadenas es si la cadena solo tiene letras , guiones bajos y números en la cadena, por lo que en su caso el espacio no cumple con los criterios.
Al usar el intérprete, hay un caso en el que puede terminar apuntando al mismo objeto incluso cuando la cadena no cumple con los criterios anteriores, asignaciones múltiples.
In [51]: a,b = ''abc abc'',''abc abc''
In [52]: id(a)
Out[52]: 139806688801768
In [53]: id(b)
Out[53]: 139806688801768
In [54]: a is b
Out[54]: True
Mirando la
fuente codeobject.c
para decidir los criterios que vemos,
NAME_CHARS
decide qué se puede internar:
#define NAME_CHARS /
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz"
/* all_name_chars(s): true iff all chars in s are valid NAME_CHARS */
static int
all_name_chars(unsigned char *s)
{
static char ok_name_char[256];
static unsigned char *name_chars = (unsigned char *)NAME_CHARS;
if (ok_name_char[*name_chars] == 0) {
unsigned char *p;
for (p = name_chars; *p; p++)
ok_name_char[*p] = 1;
}
while (*s) {
if (ok_name_char[*s++] == 0)
return 0;
}
return 1;
}
Siempre se compartirá una cadena de longitud 0 o 1, como podemos ver en la función
PyString_FromStringAndSize
en la fuente
stringobject.c
.
/* share short strings */
if (size == 0) {
PyObject *t = (PyObject *)op;
PyString_InternInPlace(&t);
op = (PyStringObject *)t;
nullstring = op;
Py_INCREF(op);
} else if (size == 1 && str != NULL) {
PyObject *t = (PyObject *)op;
PyString_InternInPlace(&t);
op = (PyStringObject *)t;
characters[*str & UCHAR_MAX] = op;
Py_INCREF(op);
}
return (PyObject *) op;
}
No está directamente relacionado con la pregunta, pero para aquellos interesados,
PyCode_New
también de la fuente
codeobject.c
muestra cómo se
codeobject.c
más cadenas al construir un objeto de código una vez que las cadenas cumplen los criterios en
all_name_chars
.
PyCodeObject *
PyCode_New(int argcount, int nlocals, int stacksize, int flags,
PyObject *code, PyObject *consts, PyObject *names,
PyObject *varnames, PyObject *freevars, PyObject *cellvars,
PyObject *filename, PyObject *name, int firstlineno,
PyObject *lnotab)
{
PyCodeObject *co;
Py_ssize_t i;
/* Check argument types */
if (argcount < 0 || nlocals < 0 ||
code == NULL ||
consts == NULL || !PyTuple_Check(consts) ||
names == NULL || !PyTuple_Check(names) ||
varnames == NULL || !PyTuple_Check(varnames) ||
freevars == NULL || !PyTuple_Check(freevars) ||
cellvars == NULL || !PyTuple_Check(cellvars) ||
name == NULL || !PyString_Check(name) ||
filename == NULL || !PyString_Check(filename) ||
lnotab == NULL || !PyString_Check(lnotab) ||
!PyObject_CheckReadBuffer(code)) {
PyErr_BadInternalCall();
return NULL;
}
intern_strings(names);
intern_strings(varnames);
intern_strings(freevars);
intern_strings(cellvars);
/* Intern selected string constants */
for (i = PyTuple_Size(consts); --i >= 0; ) {
PyObject *v = PyTuple_GetItem(consts, i);
if (!PyString_Check(v))
continue;
if (!all_name_chars((unsigned char *)PyString_AS_STRING(v)))
continue;
PyString_InternInPlace(&PyTuple_GET_ITEM(consts, i));
}
Esta respuesta se basa en asignaciones simples que utilizan el intérprete cpython, en lo que respecta a la pasantía en relación con funciones o cualquier otra funcionalidad fuera de las asignaciones simples, que no se preguntó ni respondió.
Si alguien con una mayor comprensión del código c tiene algo que agregar, no dude en editarlo.
Aquí hay una explicación mucho más exhaustiva de toda la secuencia interna.
Esta pregunta ya tiene una respuesta aquí:
Me he dado cuenta de que agregar un espacio a cadenas idénticas hace que se comparen con el uso desigual, mientras que las versiones sin espacio se comparan igual.
a = ''abc''
b = ''abc''
a is b
#outputs: True
a = ''abc abc''
b = ''abc abc''
a is b
#outputs: False
He leído
esta pregunta sobre comparar cadenas con
==
y
is
.
Creo que esta es una pregunta diferente porque el carácter de espacio está cambiando el comportamiento, no la longitud de la cadena.
Ver:
a = ''abc''
b = ''abc''
a is b # True
a = ''gfhfghssrtjyhgjdagtaerjkdhhgffdhfdah''
b = ''gfhfghssrtjyhgjdagtaerjkdhhgffdhfdah''
a is b # True
¿Por qué agregar un espacio a la cadena cambia el resultado de esta comparación?