Python中的字符串标识比较

时间:2011-01-31 16:44:35

标签: python cpython

我最近在生产系统中发现了一个潜在的错误,其中使用身份运算符比较了两个字符串,例如:

if val[2] is not 's':

我想这无论如何都会工作,因为据我所知CPython将短不可变字符串存储在同一位置。我已将其替换为!=,但我需要确认之前通过此代码的数据是否正确,因此我想知道这是否一直有效,或者它是否有时有效。

据我所知,Python版本一直是2.6.6,上面的代码似乎是唯一使用is运算符的地方。

有没有人知道这一行总是是否像程序员一样工作?

修改 因为这对未来的读者来说无疑是非常具体和无益的,我会问一个不同的问题:

我应该在哪里确切地确认Python实现的行为? CPython的源代码中的优化是否易于理解?有什么提示吗?

5 个答案:

答案 0 :(得分:3)

当您只想比较两个对象而不检查这些对象是否相同时,您当然不应该使用is / is not运算符。

虽然python永远不会创建一个与现有字符串具有相同内容的新字符串对象(因为字符串是不可变的),并且由于这一点,等式和标识是等价的,但我不会依赖它,尤其是对于那里有大量的python实现。

答案 1 :(得分:3)

您可以查看2.6.x的CPython代码:http://svn.python.org/projects/python/branches/release26-maint/Objects/stringobject.c

看起来像一个字符的字符串是专门处理的,每个不同的字符串只存在一次,所以你的代码是安全的。这是一些关键代码(摘录):

static PyStringObject *characters[UCHAR_MAX + 1];

PyObject *
PyString_FromStringAndSize(const char *str, Py_ssize_t size)
{
    register PyStringObject *op;
    if (size == 1 && str != NULL &&
        (op = characters[*str & UCHAR_MAX]) != NULL)
    {
        Py_INCREF(op);
        return (PyObject *)op;
    }

...

答案 2 :(得分:3)

正如人们已经注意到的那样,对于在python(或CPython,无论如何)中创建的字符串应该总是如此,但如果你使用的是C扩展,那么情况并非如此。

作为一个快速反例:

import numpy as np

x = 's'
y = np.array(['s'], dtype='|S1')

print x
print y[0]

print 'x is y[0] -->', x is y[0]
print 'x == y[0] -->', x == y[0]

这会产生:

s
s
x is y[0] --> False
x == y[0] --> True

当然,如果没有使用任何类型的C扩展,你可能是安全的...我不会指望它,但是......

编辑:作为一个更简单的例子,如果用struct以任何方式对事物进行了腌制或打包,它就不会成立。

e.g:

import pickle
x = 's'
pickle.dump(x, file('test', 'w'))
y = pickle.load(file('test', 'r'))

print x is y
print x == y

另外(为了清晰起见,使用不同的字母,因为格式化字符串需要"s"):

import struct
x = 'a'
y = struct.pack('s', x)

print x is y
print x == y

答案 3 :(得分:2)

此行为将始终适用于空字符和单字符latin-1字符串。来自unicodeobject.c:

PyObject *PyUnicode_FromUnicode(const Py_UNICODE *u,
                                Py_ssize_t size)
{
.....
    /* Single character Unicode objects in the Latin-1 range are
       shared when using this constructor */
    if (size == 1 && *u < 256) {
        unicode = unicode_latin1[*u];

此片段来自Python 3,但在早期版本中可能存在类似的优化。

答案 4 :(得分:0)

当然,它是由于自动短字符串实习(与python源中的常量相同,因为文字&#39;是),但在这里使用身份是非常愚蠢的。

Python是关于鸭子类型的,可以使用任何看起来像字符串的对象,例如,如果val[2]实际上是u"s",则相同的代码会失败。