在满足条件之前不断迭代列表的最 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.listdirfnmatch.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 方式?的主要内容,如果未能解决你的问题,请参考以下文章

只要基于前一个元素的条件为真,“Pythonic”方式就可以从可迭代对象中返回元素

迭代列表直到条件python [关闭]

有条件返回函数的最pythonic方法是啥

逻辑组合布尔值列表的最“pythonic”方式是啥?

从列表中弹出随机元素的最pythonic方法是啥?

检测循环最终迭代的Pythonic方法[重复]