在'if'子句中使用'in'时是元组还是列表? [英] Tuple or list when using 'in' in an 'if' clause?

查看:143
本文介绍了在'if'子句中使用'in'时是元组还是列表?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

哪种方法更好?使用元组,例如:

Which approach is better? Using a tuple, like:

if number in (1, 2):

或列表,例如:

if number in [1, 2]:

建议将哪一种用于此类用途,为什么(在逻辑和性能上都如此)?

Which one is recommended for such uses and why (both logical and performance wise)?

推荐答案

CPython解释器将第二种形式替换为第一种.

The CPython interpreter replaces the second form with the first.

这是因为从常量加载元组是一个操作,但是列表将是3个操作;加载两个整数内容并构建一个新的列表对象.

That's because loading the tuple from a constant is one operation, but the list would be 3 operations; load the two integer contents and build a new list object.

由于您使用的是无法访问的列表文字,因此它将替换为元组:

Because you are using a list literal that isn't otherwise reachable, it is substituted for a tuple:

>>> import dis
>>> dis.dis(compile('number in [1, 2]', '<stdin>', 'eval'))
  1           0 LOAD_NAME                0 (number)
              3 LOAD_CONST               2 ((1, 2))
              6 COMPARE_OP               6 (in)
              9 RETURN_VALUE        

在这里,第二个字节码在 one 步骤中将(1, 2)元组作为常量加载.将其与创建成员资格测试中未使用的列表对象进行比较:

Here the second bytecode loads a (1, 2) tuple as a constant, in one step. Compare this to creating a list object not used in a membership test:

>>> dis.dis(compile('[1, 2]', '<stdin>', 'eval'))
  1           0 LOAD_CONST               0 (1)
              3 LOAD_CONST               1 (2)
              6 BUILD_LIST               2
              9 RETURN_VALUE        

长度为N的列表对象需要N + 1步.

Here N+1 steps are required for a list object of length N.

这种替换是CPython特定的窥孔优化.请参见 Python/peephole.c.那么,对于 other 的Python实现,您要坚持使用不可变的对象.

This substitution is a CPython-specific peephole optimisation; see the Python/peephole.c source. For other Python implementations then, you want to stick with immutable objects instead.

也就是说,在使用Python 3.2及更高版本时, best 选项是使用 set文字:

That said, the best option when using Python 3.2 and up, is to use a set literal:

if number in {1, 2}:

因为窥视孔优化器将替换为frozenset()对象,并且针对集合的成员资格测试是O(1)常数操作:

as the peephole optimiser will replace that with a frozenset() object and membership tests against sets are O(1) constant operations:

>>> dis.dis(compile('number in {1, 2}', '<stdin>', '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.

This optimization was added in Python 3.2 but wasn't backported to Python 2.

因此,Python 2优化器无法识别此选项,从内容中构建setfrozenset的成本几乎可以保证比使用元组进行测试的成本更高.

As such, the Python 2 optimiser doesn't recognize this option and the cost of building either a set or frozenset from the contents is almost guaranteed to be more costly than using a tuple for the test.

Set成员资格测试为O(1)并且快速;对元组进行测试是O(n)最坏的情况.尽管再次测试一组必须计算哈希值(较高的不变成本,为不可变元组缓存),但针对元组除第一个元素之外进行测试的成本始终会更高.因此,平均而言,集合可以更快地实现:

Set membership tests are O(1) and fast; testing against a tuple is O(n) worst case. Although testing agains a set has to calculate the hash (higher constant cost, cached for immutable tupes), the cost for testing against a tuple other than the first element is always going to be higher. So on average, sets are easily faster:

>>> import timeit
>>> timeit.timeit('1 in (1, 3, 5)', number=10**7)  # best-case for tuples
0.21154764899984002
>>> timeit.timeit('8 in (1, 3, 5)', number=10**7)  # worst-case for tuples
0.5670104179880582
>>> timeit.timeit('1 in {1, 3, 5}', number=10**7)  # average-case for sets
0.2663505630043801
>>> timeit.timeit('8 in {1, 3, 5}', number=10**7)  # worst-case for sets
0.25939063701662235

这篇关于在'if'子句中使用'in'时是元组还是列表?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆