【发布时间】:2020-09-22 08:04:56
【问题描述】:
Python 3.8(或 CPython 3.8?)添加了警告
SyntaxWarning: "is" with a literal. Did you mean "=="?
代码0 is 0。
我理解警告,并且知道is 和== 之间的区别。
但是,我也知道 CPython 会缓存小整数的对象并在其他情况下共享它。
(出于好奇,我再次checked the code (header)。
小整数缓存在tstate->interp->small_ints 中。
0 和 1 更加特殊,全局存储在 _PyLong_Zero 和 _PyLong_One 中。
ints 的所有新创建都是通过 PyLong_FromLong 进行的,首先检查它是否是一个小整数并缓存。)
鉴于这个背景,如果你知道你有一个int 对象,你可以说检查x is 0 应该是安全的,对吧?此外,您可以得出0 is 0 应该始终是True,对吗?或者这是 CPython 的实现细节,其他解释器不遵循这个?哪个口译员不遵守这个?
尽管有这个更通用的问题(我只是好奇),请考虑这个更具体的(示例)代码:
def sum1a(*args):
y = 0
for x in args:
if y is 0:
y = x
else:
y = y + x
return y
对比:
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): ...。这行得通吗?这是个好主意吗?
【问题讨论】:
-
您是否考虑过使用
or子句(例如if y is None or y == 0:)或仅检查虚假性(例如if not y:)?我怀疑警告的原因是缓存的整数被视为不应该依赖的特定于实现的行为。 -
y == 0通常不起作用(例如,对于 Numpy/TensorFlow 数组)。not y也没有。 -
缓存小整数是不保证 - 这是一个可以禁用的编译时选项。但是,我希望
0 is 0(该代码字面意思,而不是来自任意来源的零)总是正确的,因为两个零都是同一个编译代码对象的一部分,因此总是会组合成一个常量。 -
@jasonharper 常量折叠也是实现定义的。确实,任何合理的 python 实现都会将其组合在一个常量中,但我可以编写一个实现,为每个发现的事件创建一个新的
0,这不会错,只是很傻。 -
我上面链接的帖子表明,在某些情况下,CPython 小整数缓存也可以被击败。永远不要依赖实现细节!
标签: python python-3.x cpython python-internals