我注意到为相同的字符串添加空格使得它们比较不相等is
,而非空间版本比较相等.
a = 'abc'
b = 'abc'
a is b
#outputs: True
a = 'abc abc'
b = 'abc abc'
a is b
#outputs: False
我已经阅读了关于将字符串与==
和进行比较的问题is
.我认为这是一个不同的问题,因为空格字符正在改变行为,而不是字符串的长度.看到:
a = 'abc'
b = 'abc'
a is b # True
a = 'gfhfghssrtjyhgjdagtaerjkdhhgffdhfdah'
b = 'gfhfghssrtjyhgjdagtaerjkdhhgffdhfdah'
a is b # True
为什么在字符串中添加空格会改变此比较的结果?
1> Padraic Cunn..:
python解释器根据某些条件缓存一些字符串,第一个abc
字符串被缓存并用于两者,但第二个字符串不是.它是由小整数同-5
来256
.
由于字符串被扣留/高速缓存分配a
和b
对"abc"
品牌a
和b
因此使用指向相同的对象在内存中is
,检查如果两个对象实际上是相同的对象,返回True
.
第二个字符串abc abc
未缓存,因此它们是内存中两个完全不同的对象,因此使用is
返回进行身份检查False
.这一次a
是不是 b
.它们都指向内存中的不同对象.
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
缓存字符串的条件是字符串在字符串中只有字母,下划线和数字,因此在您的情况下,空格不符合条件.
使用解释器有一种情况,即使字符串不符合上述条件,多个赋值,您也可以最终指向同一个对象.
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
查看 codeobject.c来决定我们看到的标准来NAME_CHARS
决定可以实现的内容:
#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;
}
我们可以PyString_FromStringAndSize
在stringobject.c源代码中看到长度为0或1的字符串.
/* 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;
}
不直接相关的问题,但对于那些有兴趣PyCode_New
从还codeobject.c
源展示了如何更弦建设codeobject时一旦弦满足标准被拘留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));
}
这个答案基于使用cpython解释器的简单赋值,就函数或简单赋值之外的任何其他功能而言是实际的,没有被提及也没有被回答.
如果对c代码有更深入理解的人有任何添加的内容,请随时编辑.
还有一个更详尽的解释这里的整串实习的.
@Bakuriu,多个任务不是单一的任务,所以有一个明显的区别,一个功能在任何地方都没有提到,在问题中提出的问题和在答案中演示的是简单的任务,没有提到任何功能只是一个解释为问题.
@PadraicCunningham这不是文档或任何有关python语言的官方文档的参考.另外:请注意,在提到实习后,它提到了窥视孔优化,这正是你所说的失败的一点:在一个函数中放置一个&#39;a =&#39;abc abc&#39;`和`b =&#39;abc abc&#39;`突然`a是b `返回`True`.
注意:复制问题**确实**包含您在此处提供的信息,在[第二个答案]中(http://stackoverflow.com/a/1504848/510937).
@midkin考虑到发生这种情况是因为*没有保证*,这是无用的*.我们**不能**说如果有空格它不会被缓存,因为它*可能*现在发生,而不是在下一个错误修复版本中.不同的python实现将使用不同的方式来确定何时实习,并且相同实现的不同版本在这方面可以自由地不同意.
我决定拒绝你的答案,因为你提供*虚假信息*.如果你想添加一个段落的最后一个,你应该:1)提供了一些文件是提及或2)提供了一个链接到源代码中做到这一点*和*参考明确提到这必将改变实现和版本.正如你所说,你的答案是建议存在这样的规则并且可以依赖,**这是错误的**(至少,据我所知.如果我错了用参考证明它).
@martijnpieters,我在查看源代码之前在编辑器中引用了你的文章,在你的文章中,它在文章中声明字符串必须以下划线或字母开头,但似乎它也可以以数字开头,唯一的标准是它必须是三个之一吗?