4

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

if val[2] is not 's':

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

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

有谁知道这条线是否总是按照程序员的意图工作?

编辑: 因为这无疑对未来的读者非常具体且无益,所以我会问一个不同的问题:

我应该在哪里寻找绝对确定的 Python 实现的行为?CPython 源代码中的优化是否易于理解?有小费吗?

4

5 回答 5

3

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

虽然 python 永远不会创建与现有对象具有相同内容的新字符串对象是有道理的(因为字符串是不可变的)并且因此相等和身份是等价的,但我不会依赖它,尤其是对于大量的 python那里的实现。

于 2011-01-31T16:53:58.260 回答
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;
    }

...
于 2011-01-31T17:56:44.730 回答
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编辑:作为一个更简单的例子,如果东西以任何方式被腌制或包装,它就不成立。

例如:

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
于 2011-01-31T18:01:30.073 回答
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,但在早期版本中可能存在类似的优化。

于 2011-01-31T17:51:27.780 回答
0

由于自动短字符串实习(与 python 源代码中的常量相同,如字面量 's' 一样),它确实有效,但在这里使用身份非常愚蠢。

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

于 2013-06-28T12:25:53.293 回答