【问题标题】:Check if something is (not) in a list in Python检查 Python 列表中是否有(不)某些东西
【发布时间】:2012-05-11 11:27:51
【问题描述】:

我在Python 中有一个元组列表,并且我有一个条件,只有当元组不在列表中时我才想使用分支(如果它在列表中,那么我不想取 if 分支)

if curr_x -1 > 0 and (curr_x-1 , curr_y) not in myList: 

    # Do Something

不过,这对我来说并不适用。我做错了什么?

【问题讨论】:

  • 请注意 3 -1 > 0 and (4-1 , 5) not in []True 因此错误不是运算符优先级之一。
  • “不是真的为我工作”是什么意思?你期望会发生什么?实际发生了什么?哪些确切的列表内容会触发问题?
  • 为什么不试试myList.count((curr_x, curr_y)),如果(curr_x, curr_y)不在myList中,结果会是0
  • 击败了我,这是我 8 年前在大学时写的。如果您愿意,可以提供一个最小的可重现示例:)

标签: python list conditional membership


【解决方案1】:

该错误可能在您的代码中的其他地方,因为它应该可以正常工作:

>>> 3 not in [2, 3, 4]
False
>>> 3 not in [4, 5, 6]
True

或者使用元组:

>>> (2, 3) not in [(2, 3), (5, 6), (9, 1)]
False
>>> (2, 3) not in [(2, 7), (7, 3), "hi"]
True

【讨论】:

  • @Zack:如果你不知道这个,你可以做if not ELEMENT in COLLECTION:
  • @ninjagecko:取决于可能效率较低甚至不正确的容器类型。参见例如bloom filters
  • @nightcracker 这没有意义,因为A not in B 被简化为not B.__contains__(A),这与not A in B 被简化为not B.__contains__(A) 相同。
  • 哦,哇,我可以发誓 Python 有类似 __notcontains__ 的东西。对不起,那我说的都是废话。
  • @std''OrgnlDave 唯一可能发生的情况是not 的优先级高于in,而in 没有。考虑ast.dump(ast.parse("not A in B").body[0]) 的结果,它导致"Expr(value=UnaryOp(op=Not(), operand=Compare(left=Name(id='A', ctx=Load()), ops=[In()], comparators=[Name(id='B', ctx=Load())])))" 如果not 与A 紧密组合,人们会期望结果是"Expr(value=Compare(left=UnaryOp(op=Not(), operand=Name(id='A', ctx=Load())), ops=[In()], comparators=[Name(id='B', ctx=Load())]))",这是"(not A) in B" 的解析。
【解决方案2】:

如何在 Python 的列表中检查某些内容是否(不在)?

最便宜和最易读的解决方案是使用in 运算符(或在您的特定情况下,not in)。如文档中所述,

运算符innot in 测试成员资格。 x in s 评估为 True 如果xs 的成员,则False 否则。 x not in s 返回 x in s的否定。

另外,

运算符not in 被定义为具有in 的逆真值。

y not in x 在逻辑上与not y in x 相同。

这里有几个例子:

'a' in [1, 2, 3]
# False

'c' in ['a', 'b', 'c']
# True

'a' not in [1, 2, 3]
# True

'c' not in ['a', 'b', 'c']
# False

这也适用于元组,因为元组是可散列的(因为它们也是不可变的):

(1, 2) in [(3, 4), (1, 2)]
#  True

如果 RHS 上的对象定义了 __contains__() 方法,in 将在内部调用它,如文档的 Comparisons 部分的最后一段所述。

...innot in, 由可迭代或实现的类型支持 __contains__() 方法。例如,您可以(但不应该)这样做:

[3, 2, 1].__contains__(1)
# True

in 短路,因此如果您的元素位于列表的开头,in 的计算速度会更快:

lst = list(range(10001))
%timeit 1 in lst
%timeit 10000 in lst  # Expected to take longer time.

68.9 ns ± 0.613 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
178 µs ± 5.01 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

如果您想做的不仅仅是检查某个项目是否在列表中,还有以下选择:

  • list.index 可用于检索项目的索引。如果该元素不存在,则会引发 ValueError
  • 如果要统计出现次数,可以使用list.count

XY 问题:你考虑过sets 吗?

问自己这些问题:

  • 您是否需要多次检查某个项目是否在列表中?
  • 此检查是在循环内完成的,还是在重复调用的函数内完成的?
  • 您存储在列表中的项目是否可散列? IOW,你可以给他们打电话hash 吗?

如果您对这些问题的回答为“是”,则应改用set。在lists 上的in 成员资格测试是 O(n) 时间复杂度。这意味着 python 必须对列表进行线性扫描,访问每个元素并将其与搜索项进行比较。如果您重复执行此操作,或者列表很大,则此操作将产生开销。

set 对象,另一方面,散列它们的值以进行恒定时间成员资格检查。检查也是使用in

1 in {1, 2, 3} 
# True

'a' not in {'a', 'b', 'c'}
# False

(1, 2) in {('a', 'c'), (1, 2)}
# True

如果您很不幸,您正在搜索/未搜索的元素位于列表的末尾,python 将一直扫描到列表的末尾。从以下时间可以看出这一点:

l = list(range(100001))
s = set(l)

%timeit 100000 in l
%timeit 100000 in s

2.58 ms ± 58.9 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
101 ns ± 9.53 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

提醒一下,只要您存储和查找的元素是可散列的,这是一个合适的选项。 IOW,它们要么必须是不可变类型,要么必须是实现 __hash__ 的对象。

【讨论】:

  • 集合并不总是一个选项(例如,当有一个可变项目列表时)。对于大型集合:构建用于查找的集合无论如何都是 O(n) 时间,并且可能会使您的内存使用量翻倍。如果您还没有四处查找,那么制作/维护它并不总是最好的选择。
猜你喜欢
  • 2012-06-04
  • 2014-05-12
  • 2021-07-27
  • 2018-12-05
  • 2012-03-12
  • 1970-01-01
相关资源
最近更新 更多