当前位置: 首页 > 面试题库 >

在'if'子句中使用'in'时的元组或列表?

秦渝
2023-03-14
问题内容

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

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

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

>>> 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个步骤。

这种替换是CPython特有的窥孔优化。见Python/peephole.c资料来源。然后,对于
其他 Python实现,您要坚持使用不可变的对象。

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

if number in {1, 2}:

因为窥视孔优化器将用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

此优化是在Python
3.2
中添加的,但并未反向移植到Python
2。

因此,Python 2优化器无法识别此选项,并且几乎可以肯定构建asetfrozenset从内容构建的成本比使用元组进行测试的成本更高。

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

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


 类似资料:
  • 我有这样一个实体: 我正在使用(value,linkType)作为一个复合索引。对于给定的(v,t)元组列表,我们需要选择DB中的所有记录,使value=v,linkType=t。 基本上,我想构建这个查询:

  • 问题内容: 我知道如何将列表映射到字符串: 而且我知道我可以使用以下命令将该字符串放入IN子句中: 我需要使用MySQLDB安全地完成同一件事(避免SQL注入)。在上面的示例中,由于foostring没有作为执行参数传递,因此它很容易受到攻击。我还必须在mysql库之外引用和转义。 (有一个相关的SO问题,但是那里列出的答案对MySQLDB不起作用或容易受到SQL注入的影响。) 问题答案: 直接使

  • 我想执行sqlite查询: in子句中的值需要取自字符串数组: 以下是我的错误日志。 原因:Android。数据库sqlite。SQLiteException:接近“1”:语法错误(代码1):,编译时:从sku所在的books_表中选择book_id、book_code、ISBN、vol_no、brand、sku、title、price、lang、status(单打、5合1系列、儿童图书);在An

  • 问题内容: 我有一个数据库,其中有四列对应于开始和结束位置的地理坐标x,y。这些列是: 00 00 x1 11 我有这四个列的索引,其顺序为x0,y0,x1,y1。 我列出了大约一百种地理对组合。我将如何有效地查询此数据? 我想按照此SO答案的建议执行类似的操作,但它仅适用于Oracle数据库,不适用于MySQL: 我以为可能对索引做些什么?最好的方法是什么(即:最快的查询)?谢谢你的帮助! 笔记

  • 问题内容: 我有一个句子列表: 我需要形成双字母对,并将它们存储在变量中。问题是当我这样做时,我得到一对句子而不是单词。这是我所做的: 产生 火车站和城堡酒店不能合二为一。我想要的是 第一个句子的最后一个单词不应与第二个句子的第一个单词合并。我应该怎么做才能使其正常工作? 问题答案: 使用列表推导和zip:

  • 问题内容: 我通过执行查询得到。这必须传递给 IN 子句值的另一个查询。如何通过 HQL ? 我们可以转换到并且可以通过它,这不是一个问题。 最后,我必须将列表以或或形式传递给 IN 子句。 问题答案: 要么