CPython解释器用第一个替换第二个表单。
这是因为从常量加载元组是一个操作,但是列表将是3个操作;加载两个整数内容并构建一个新的列表对象。
因为您正在使用不可达到的列表文字,而是替换一个元组:
>>> import dis
>>> dis.dis(compile('number in [1, 2]', '', 'eval'))
1 0 LOAD_NAME 0 (number)
3 LOAD_CONST 2 ((1, 2))
6 COMPARE_OP 6 (in)
9 RETURN_VALUE
在这里,第二个字节码将一个(1,2)元组作为一个常量加载。与创建在成员身份测试中未使用的列表对象进行比较:
>>> dis.dis(compile('[1, 2]', '', 'eval'))
1 0 LOAD_CONST 0 (1)
3 LOAD_CONST 1 (2)
6 BUILD_LIST 2
9 RETURN_VALUE
这里需要N个长度为N的列表对象的步骤。
这种替代是CPython特有的窥视孔优化;请参阅Python/peephole.c source.对于其他Python实现,那么你想要坚持使用不可变对象。
也就是说,使用Python 3.2及以上版本的最佳选择是使用set literal:
if number in {1, 2}:
因为窥视孔优化器将用frozenset()对象替代,而对集合的成员资格测试是O(1)常量操作:
>>> dis.dis(compile('number in {1, 2}', '', 'eval'))
1 0 LOAD_NAME 0 (number)
3 LOAD_CONST 2 (frozenset({1, 2}))
6 COMPARE_OP 6 (in)
9 RETURN_VALUE
这个优化在Python 3.2年被添加,但没有被转载到Python 2。
因此,Python 2优化程序不能识别此选项,并且从内容构建集合或结果的成本几乎保证比使用元组进行测试的成本更高。