在Python 2.x中使用'is'运算符比较对象与空元组
我习惯在Python中看到 if obj is None:
这样的写法,最近我发现了 if obj is ():
这种写法。因为元组是不可变的,所以在Python解释器内部,把空元组当成一个单例(也就是只有一个实例)似乎是个合理的优化,这样就可以用 is
来比较,而不需要用 ==
。但是,这种做法有没有什么保证呢?从哪个版本开始有这样的保证呢?
[编辑] 这个问题很重要,因为如果空元组 ()
不是单例,并且有办法生成一个地址不同的空元组,那么使用 is {}
就会出错。如果这个特性只在Python 2.x的某个版本(x > 0)中有保证,那么如果你需要确保代码的向后兼容性,就需要知道这个x的值。此外,还需要知道在使用pypy、jython或ironpython时,这是否会导致代码出错……
4 个回答
1
这是当前版本的CPython中的一个非保证的实现细节,所以在其他Python版本中,比如Jython、IronPython、PyPy,甚至未来的CPython版本,你可能不能依赖这个特性。
在我的系统上,使用 is
和一个大列表进行比较时,似乎快了大约0.04微秒:
$ python -m timeit -s "x = range(10000)" "x is ()"
10000000 loops, best of 3: 0.0401 usec per loop
$ python -m timeit -s "x = range(10000)" "x == ()"
10000000 loops, best of 3: 0.0844 usec per loop
当然,如果你比较的是一个有自定义 __eq__()
方法的对象,情况可能会糟糕得多:
$ python -m timeit -s $'import time\nclass X(object):\n def __eq__(self, other): return time.sleep(1)\nx = X()' "x == ()"
10 loops, best of 3: 1e+03 msec per loop
不过,如果这个效率差异非常重要,我觉得这可能说明设计上有问题。
1
我们来用 id() 这个方法获取 () 的内部 ID:
>>> id(())
140180995895376
>>> empty_tuple = ()
>>> id(empty_tuple)
140180995895376 # same as the id of ()
>>> from copy import copy
>>> id(copy(empty_tuple))
140180995895376 # still the same as the id of ()
看起来在 Python 中,() 实际上是作为一个单例存储的(至少在 Python 2.6 及以上版本中是这样)。
对于 ""
这个空字符串变量也是一样的情况。
13
根据Python 2和Python 3的官方文档:
... 两个空元组的出现可能会返回同一个对象,也可能不会。
换句话说,你不能指望 () is ()
这个表达式总是返回真。