在Python中,`0 is 0`是否总是为`True`?

9
Python 3.8(或CPython 3.8?)添加了警告。
SyntaxWarning: "is" with a literal. Did you mean "=="?

对于代码0 is 0

我理解警告,并且知道is==之间的区别。

然而,我也知道CPython缓存小整数的对象并在其他情况下共享它。 (出于好奇,我刚刚检查了一下代码header)。 小整数被缓存在tstate->interp->small_ints中。 01更加特殊,全局存储在_PyLong_Zero_PyLong_One中。 所有新创建的int都是通过PyLong_FromLong进行的,该函数首先检查是否为小整数并将其缓存。)

鉴于这一背景,如果您知道您拥有一个int对象,你可以说检查x是否为0应该是安全的,对吗?此外,您可以得出0等于0应始终为True,对吗?或者这是CPython的实现细节,其他解释器不遵循这个规则?哪个解释器不遵循这个规则?
尽管存在这个更普遍的问题(我只是好奇),请考虑这个更具体的(示例)代码:
def sum1a(*args):
    y = 0
    for x in args:
        if y is 0:
            y = x
        else:
            y = y + x
    return y

Vs:

def sum1b(*args):
    y = 0
    for x in args:
        if y == 0:
            y = x
        else:
            y = y + x
    return y

对比:

def sum1c(*args):
    y = None
    for x in args:
        if y is None:
            y = x
        else:
            y = y + x
    if y is None:
        return 0
    return y

对比:

def sum2(*args):
    y = 0
    for x in args:
        y = y + x
    return y

有时我更喜欢使用sum1*而不是sum2,因为根据库的不同,sum1*可以更加高效。例如,如果参数是一个Numpy/TensorFlow/PyTorch数组,你确实可以在这里节省一次(潜在的昂贵)操作。
我更喜欢sum1a而不是sum1b的原因是sum1b会在某些输入上出现错误。例如,如果输入是一个Numpy数组,这将无法工作。
当然,你可以使用sum1c代替sum1a。然而,sum1a更短。所以这更好吗?
如果对于原始问题的答案应该始终有效,并且如果你同意sum1a是最佳选择,那么你如何消除警告呢?有没有简单的解决方法?通常情况下,我认为警告可能很有用。所以我不想完全禁用它。我只想为这个特定的语句禁用它。
也许我可以把它封装在一个函数中:
def is_(a, b):
    return a is b

然后只需使用if is_(y, 0): ...。这样行吗?这是个好主意吗?


1
你是否考虑过使用“or”语句(例如,“if y is None or y == 0:”)或者只检查假值(例如,“if not y:”)?我猜测警告的原因是缓存整数被视为实现特定的行为,不能依赖它。 - Green Cloak Guy
1
通常情况下(例如对于Numpy/TensorFlow数组),y == 0不起作用。not y也是如此。 - Albert
1
小整数的缓存不能保证 - 它是一个编译时选项,可以被禁用。然而,我希望0 is 0(字面上的代码,而不是来自任意源的零)始终为真,因为两个零都是同一个编译代码对象的一部分,因此总是会被合并成一个单一的常量。 - jasonharper
1
@jasonharper 常量折叠也是实现定义的。虽然任何合理的Python实现都会将其组合成单个常量,但我可以编写一个实现,为每个找到的出现创建一个新的0,这并不是错误的,只是愚蠢而已。 - Dimitris Fasarakis Hilliard
3
我链接的帖子显示,CPython小整数缓存在某些情况下也可能会失效。永远不要依赖于实现细节! - wim
显示剩余10条评论
1个回答

9
不是。以Rust实现的Python (Rust implementation) 为例,返回了 False
>>>>> 0 is 0
False

虽然这种写法目前还不算错误,但我希望在未来的版本中会改变(实际上已经有改变了!)。

is 调用了 id 函数,它的唯一规定是返回给定对象的唯一且恒定的标识符。而对于数值类型的源代码表示形式(如此处的 0),是否映射到一个独特的对象由具体实现定义。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接