当前位置: 首页 > 面试题库 >

为什么空格会影响相等字符串的身份比较?

司空坚
2023-03-14
问题内容

我注意到,在相同的字符串中添加空格会使它们使用比较不相等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

为什么在字符串中添加空格会更改此比较的结果?


问题答案:

python解释器根据某些条件缓存一些字符串,第一个abc字符串被缓存并用于两者,而第二个则不。从in-5到int的小整数也是一样256

由于字符串被扣留/高速缓存分配ab"abc"品牌ab在存储器中指向同一对象,以便使用is,它检查是否两个对象实际上是相同的对象,返回True

第二个字符串abc abc不会被缓存,因此它们是内存中的两个完全不同的对象,因此使用isreturn进行身份检查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源头开始显示,一旦字符串满足中的条件,在构建代码对象时如何插入更多字符串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代码有更深入的了解的人可以添加任何内容,请随时进行编辑。

还有一个更详尽的解释这里的整串实习的。



 类似资料:
  • 我试图评估比较两个字符串是否会随着它们的长度增加而变慢。我的计算表明比较字符串应该需要一个摊销常数时间,但我的Python实验产生了奇怪的结果: 下面是字符串长度(1到400)与时间(毫秒)的关系图。自动垃圾收集已禁用,并且gc。在每次迭代之间运行collect。 我每次比较100万随机字符串,计算匹配如下。这个过程重复50次,然后取所有测量时间的最小值。 什么可能解释长度64左右突然增加? 注意

  • 问题内容: 在JavaScript中,为什么要评估为,但要评估为? 我在一个文本输入字段进行数字运算,和我正在检查该字段为,或。当有人在该字段中输入几个空格时,我的验证在这三个字段上均失败,并且我对为什么它无法通过检查感到困惑。 问题答案: JavaScript将一个空字符串解释为0,然后失败了isNAN测试。您可以先在字符串上使用parseInt,它不会将空字符串转换为0。然后结果应为isNAN

  • 我有一个包含以下条目的Java配置文件 这是一个编入工作了6年的罐子的资源。突然之间,这种比较似乎并不等同于布尔真。 我已经检查了Windows行尾和空格。这是Bash5.0.17,它相当新。它在Solaris上运行。似乎足够简单,我们不想编辑脚本,如果可能的话。还有什么其他的解释呢?

  • 问题内容: 我有一个Android应用程序,我想检查安装的应用程序名称是否与传递给包含此代码的函数的字符串匹配。代码和示例如下: 假设您打过电话,并且手机上的应用程序名称与返回的名称相同。但是,它永远不会。我记录了结果,它应该匹配,但事实并非如此。任何人都可以请问我为什么这行不通吗? 问题答案: 使用String的equals()方法代替==运算符来比较字符串: 在Java中,新手遇到的最常见错误

  • 这里有2个测量: 正如您所见,比较两个匹配的字符串比比较两个大小相同但不匹配的字符串要快。这是非常令人不安的:在字符串比较期间,我认为python是逐字符测试字符串的,所以应该比更长,因为它需要对1进行4次测试才能进行不匹配的比较。可能比较是基于哈希的,但在这种情况下,两种比较的计时应该相同。 你知道为什么吗?

  • 我在写Fizz Buzz的时候遇到了一个意想不到的互动。如果我执行