我正在研究包含数千个出现的代码库foo in list(bar)
,例如:
-
作为布尔表达式:
if foo in list(bar) or ...: ...
-
在 for 循环中:
for foo in list(bar): ...
-
在生成器表达式中:
",".join(str(foo) for foo in list(bar))
是否存在这样的场景(例如给定版本的 Python、类型检查器的已知行为等),其中foo in list(bar)
不仅仅是内存昂贵的版本foo in bar
?我在这里遗漏了什么?
4
5 个回答
5
我有时会做/看到bar
在循环中被修改的情况,例如:
bar = {1, 2, 3}
for foo in list(bar):
bar.add(foo + 1)
有您的替代者,情况就提高了RuntimeError: Set changed size during iteration
。
Python
for k in list(_config_vars):
if k.startswith(_INITPRE):
del _config_vars[k]
(很多是出于上述原因,但不是全部)。
3
-
这难道不是使用 pop 的副作用吗,而问题是询问初始迭代?
–
-
1@OneCricketeer 不确定你说的初始迭代是什么意思。我认为他们问的是删除列表调用是否安全。这是我实际使用/看到过的一种技术,我认为这可能就是他们的代码中所使用的。在那里删除它是不安全的。
– -
2这可能是最有可能的例子,你会看到一个有效的用例
for x in list(something)
–
|
这里有所不同:
bar = iter('bar')
foo = 'b'
if foo in list(bar):
print(*bar)
这会打印一个空行。使用替换a r
后,它会打印。
|
如果foo
和bar
都是字符串,成员测试的行为会有所不同。<str> in <str>
检查子字符串,而<str> in list(<str>)
检查字符。
>>> foo = 'ab'
>>> bar = 'abc'
>>> foo in bar # Substring
True
>>> foo in list(bar) # Element (character)
False
关于循环,不要让语法欺骗你,它与布尔表达式不同。循环使用协议(例如__iter__
),而布尔表达式使用协议(例如__contains__
,或者它会回退到可迭代协议)
|
什么时候bar
是生成器,bar
并且list(bar)
可能存在其他差异。以下是一个例子:
def abc():
yield 'a'
yield 'b'
yield 'c'
raise Exception('Incorrect data')
foo = 'a'
if foo in abc():
print('without list()') # <-- This is printed (`raise` is not reached)
if foo in list(abc()): # <-- This raises an exception
print('with list()') # <-- So this is never reached
在第一种情况下,if foo in abc()
不会耗尽生成器。由于惰性求值,一旦'a'
产生,生成器将不会再产生任何项。
在第二种情况下,if foo in list(abc())
耗尽生成器,从而引发异常。因此'with list()'
永远不会被打印。
2
-
我认为这个例外使这个例子不再是最优的,因为这意味着他们的代码中可能没有这种情况。
– -
(换句话说:与我在 juanpa 的回答下评论的理由相同)
–
|
因此,任何人都可以创建一个以这种方式工作的类型:
>>> class Crazy:
... def __iter__(self):
... yield from range(10)
...
>>> 3 in Crazy()
True
>>> class Crazy:
... def __iter__(self): yield from range(10)
... def __contains__(self, item): raise TypeError("sorry, cant do that")
...
>>> 3 in Crazy()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in __contains__
TypeError: sorry, cant do that
>>> 3 in list(Crazy())
True
>>>
大多数内置类型都“表现良好”。然而,一个重要的例外可能是str
和其他类似类型(bytes
,bytearray
):
>>> 'foo' in 'foobar'
True
>>> 'foo' in list('foobar')
False
此外,至少对于一个流行的图书馆来说,numpy
情况并非如此:
>>> import numpy as np
>>> arr = np.array([[1,2,3], [4,5,6]])
>>> arr
array([[1, 2, 3],
[4, 5, 6]])
>>> 6 in arr
True
>>> 6 in list(arr)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
>>>
2
-
考虑到他们的情况,我认为 NumPy 示例是倒退的。既然
6 in list(arr)
不起作用,那可能不是他们遇到的情况。它并没有表明删除list()
可能会有问题,而是表明添加它可能有问题。(另一个例子是3 in list(itertools.count())
……由于倒退,我决定不发布它。)
–
-
1@nocomment 说得好,但我只是想强调,第三方库没有义务确保这些一致性
–
|
bar
实际情况in
。类对象的行为由其如何覆盖来定义__contains__
。可能bar.__contains__(foo)
不等同于list(bar).__contains__(foo)
–
–
–
list
那里的一些原因,但我认为这仍然是一种罕见的需求,因此数千次发生仍然令人惊讶。–
|