Python 使用'时的元组或列表;在';在';如果';条款

Python 使用'时的元组或列表;在';在';如果';条款,python,list,optimization,tuples,python-internals,Python,List,Optimization,Tuples,Python Internals,哪种方法更好?使用元组,如: if number in (1, 2): 或者一个列表,比如: if number in [1, 2]: 推荐哪一种用于此类用途以及原因(逻辑和性能方面) CPython解释器将第二种形式替换为第一种形式 这是因为从常量加载元组是一个操作,但列表将是3个操作;加载两个整数内容并构建一个新的列表对象 由于您使用的是无法访问的列表文字,因此它将替换为元组: >>> import dis >>> dis.dis(compile('n

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

if number in (1, 2):
或者一个列表,比如:

if number in [1, 2]:
推荐哪一种用于此类用途以及原因(逻辑和性能方面)

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

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

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

>>> 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        
这里,长度为N的列表对象需要N+1个步骤

这种替代是一种特定于CPython的窥视孔优化;看。对于其他Python实现,您希望使用不可变对象

也就是说,在使用Python 3.2及更高版本时,最好的选择是使用set-literal:

由于窥视孔优化程序将用
frozenset()
对象替换该对象,因此针对集合的成员资格测试是O(1)常量操作:

>>> 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

第三个选项:
set
(具有更快的成员资格测试)。CPython将进行一些内部优化,并将列表文本存储为元组…第四个选项:
frozenset
,其成员资格测试成本与set相同,
O(1)
,但因为它是不可变的,python解释器知道它需要分配的哈希表的确切大小,而不是为其他元素留出空间。@Icardor:但仅在python 3中;在Python2中使用set literal或
frozenset([…])
表达式意味着必须首先创建对象,这是一个比针对等长元组的成员资格测试更昂贵的操作。@sapam:在这种情况下,简单的相等测试将击败这两个测试。这里需要考虑平均成本,而不是最佳情况。对于2个或更多元素,该组将获胜。假设它是一个用字节码存储的常量。我听说有一位著名的Python专家提供了一些youtube视频,解释了(C)Python可以做的所有优化…:p@JonClements:有些,不是全部-P@IceArdor:否,因为构建该集的成本大于针对元组进行测试的成本。元组测试是O(N)最坏的情况,而创建集合的成本保证为O(N)加上成员测试。这与文字语法无关,这与优化器识别您可以用
frozenset
常量替换集合有关。许多真正关心效率的Python程序将预构建匹配的数据结构、regexp等。因此,如果
ACCEPTABLE={1,2}
是预定义的(作为全局或幻影kwarg),那么唯一的开销是测试,而不是测试对象的构造。@endolith:Set文本更好。我的答案中的timeit测试表明,在最坏的情况下,它们比使用元组更快,在最好的情况下,它们几乎是等价的。这是因为集合不需要比较每个元素。
if number in {1, 2}:
>>> 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
>>> 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