在满足条件之前不断迭代列表的最 Pythonic 方式?
Posted
技术标签:
【中文标题】在满足条件之前不断迭代列表的最 Pythonic 方式?【英文标题】:Most pythonic way to continually iterate over a list until a condition is met? 【发布时间】:2015-12-21 05:26:58 【问题描述】:我有一个包含异步更改的字符串值的列表。我想不断检查列表,直到列表中没有项目包含某个子字符串,然后执行命令。
为了让事情不那么抽象,我将解释我的具体情况。我正在使用 selenium 自动下载几个文件,在所有文件下载完成后,我想关闭 webdriver。我目前对该问题的解决方案如下:
while True:
for file in os.listdir(download_dir):
if 'tmp' in file.lower():
break
else:
driver.quit()
break
基本上,检查列表中的任何元素(在本例中为下载目录中的文件名)是否包含“tmp”,以及是否再次开始检查。如果已检查整个列表并且其中没有包含 'tmp' 的元素,则退出 Web 驱动程序并退出循环。
这个解决方案工作正常,但是在我曾经提出的一个堆栈溢出线程中(请参阅here),许多人说,虽然这样的方法可以工作,但它可能是一种糟糕的方法.
所以我的问题是,有没有更 Pythonic 的方式来做到这一点?或者,如果不是一般的更好的方法?我的解决方案没有问题,但其他答案中的一些 cmets 让我有一种我应该这样做的轻微感觉。
【问题讨论】:
看看watchdog
package。
@TomKarzes,else 不打算与 if 语句配对,它与 for 循环配对。请参阅 nmichaels 对链接问题的回答 here。
@alecxe,感谢您的建议。我认为这肯定会奏效,但如果可以的话,我想不使用其他软件包,特别是如果它只是为了这件事。
这个问题是一个 XY 问题。至少使用dircache。另见this SO QA
您问题的标题与问题的一种解决方案有关,而不是与问题有关,即它是XYproblem :)
【参考方案1】:
如果您可以使用区分大小写的匹配,则可以使用glob
模块。如果没有,您可以组合 os.listdir
和 fnmatch.fnmatch
来创建不区分大小写的匹配:
import glob
import fnmatch
import os
download_dir = "/tmp"
def glob_insensitive(path, pattern):
return fnmatch.filter(os.listdir(path), pattern)
while glob.glob( os.path.join(download_dir, "*tmp*") ):
pass # or better sleep
driver.quit()
while glob_insensitive( download_dir, "*tmp*" ):
pass # or better sleep
driver.quit()
对于一般样式问题,我会使用 any
和生成器表达式:
while [ f for f in os.listdir(download_dir) if "tmp" in f.lower() ]:
pass# or better sleep some time
driver.quit()
生成器表达式比构造列表要好,但如果它为空,则无法轻松测试。
【讨论】:
【参考方案2】:我可能错了,但对我来说,“pythonic”对于任何说 python 的人来说都意味着简单易懂。您的代码相当小,几乎所有会说 python 的人都能理解它的作用。
但是,如果您的代码需要一段解释正在发生的事情以及您为什么这样做,那么问题就来了。相反,你可以写一些不言自明的东西。
# we assume the file itself doesn't contain 'tmp'
is_ready = lambda filename: not ('tmp' in filename.lower())
while True:
if all(is_ready(a_download) for a_download in os.listdir(download_dir)):
driver.quit()
break
【讨论】:
【参考方案3】:我认为使用一点 itertools 魔法来避免 while 循环更符合 Python 风格:
from itertools import repeat
from itertools import chain
for file in chain.from_iterable(repeat(os.listdir(download_dir))):
if 'tmp' in file.lower():
driver.quit()
break
基本上,我们使用 repeat 来创建一个迭代器,它永远返回 listdir 迭代器。现在我们有一个无限的惰性迭代器列表,基本上是一个迭代器上的迭代器。然后 from_iterable 将迭代器上的迭代器转换为单链迭代器。
由于我们只有一个循环,我们再也不用担心双断问题,可以大大简化控制逻辑。
【讨论】:
以上是关于在满足条件之前不断迭代列表的最 Pythonic 方式?的主要内容,如果未能解决你的问题,请参考以下文章