Python 3.x 在单个表达式中筛选列表实例(或对象)中的元素
当使用Python 3.x 在单个表达式中筛选列表实例(或对象)中的元素,python-3.x,list,list-comprehension,Python 3.x,List,List Comprehension,当使用os.walk时,需要我修改生成的目录列表以过滤它们,我想知道是否有一种很好的方法可以做到这一点: for path, dirs, files in os.walk("."): for excl in EXCLUDES: if excl in dirs: dirs.remove(excl) 我知道我可以喜欢[e for e in dirs if e not in EXCLUDES],但当我需要修改原始实例时,这将不起作用
os.walk
时,需要我修改生成的目录列表以过滤它们,我想知道是否有一种很好的方法可以做到这一点:
for path, dirs, files in os.walk("."):
for excl in EXCLUDES:
if excl in dirs:
dirs.remove(excl)
我知道我可以喜欢[e for e in dirs if e not in EXCLUDES]
,但当我需要修改原始实例时,这将不起作用
写s.th。比如[dirs.remove(e)for e in dirs if e in EXCLUDES]
确实有效,但我滥用了列表理解-pylint
不喜欢它(出于某种原因),Python的未来版本甚至可能会忽略这个表达式,因为不使用生成的值
那么,在提供的场景中,有没有一种方法可以对类似的列表进行操作
dirs.remove_items(e for e in EXCLUDES if e in dirs)
或者更一般地说:是否有(推荐的)方法以“for each”语义运行命令,如
fn(e) for e in container
那么,在提供的场景中,有没有一种方法可以对类似的列表进行操作
dirs.删除项目(如果dirs中有e,则排除e中的e)
否。列表没有内置的remove_items
或remove_all
功能。对于集合,您有执行此任务的交叉点\u更新
或-=
:
>>> EXCLUDES = {"a", "b", "c"}
>>> dirs = {"a", "x", "y"}
>>> dirs -= EXCLUDES
>>> dirs == {'x', 'y'}
True
或者更一般地说:是否有(推荐的)方法以“for each”语义运行命令,例如fn(e)for e in container
是:如果fn(e)
是纯的,使用列表理解,否则,写:
for e in container:
fn(e)
一些评论
编辑下面的@ShadowRanger评论。
各国:
当topdown为True时,调用方可以就地修改dirnames列表(可能使用del或slice赋值),walk()将只递归到名称保留在dirnames中的子目录中;这可以用于删除搜索、强制执行特定的访问顺序,甚至可以在再次恢复walk()之前通知walk()调用方创建或重命名的目录。自顶向下为False时修改dirnames对漫游行为没有影响,因为在自下而上模式下,dirnames中的目录是在dirpath本身生成之前生成的
我不明白OP的目标是删减搜索。以下评论不适用于这种情况,但在一般情况下仍然适用。请参阅@ShadowRanger的答案,以使用切片分配修剪搜索
编辑结束
正如您所说,使用列表理解(或map
)来产生副作用是个坏主意。列表理解返回一个列表,句点
但是,没有什么可以阻止您将您编写的列表分配给
dirs
本身:
dirs = [e for e in dirs if e not in EXCLUDES]
您不是在删除项目,而是在创建一个新列表(如果我没有弄错的话,这就是列表理解的要点)。如果您担心创建新列表的成本,可以使用生成器:
>>> EXCLUDES = {"a", "b", "c"}
>>> dirs = ["a", "x", "y"]
>>> dirs = (e for e in dirs if e not in EXCLUDES)
>>> dirs
<generator object <genexpr> at ...>
>>> list(dirs)
['x', 'y']
但不适用于减少:
>>> from functools import reduce
>>> dirs = reduce(lambda acc, f: acc if f in EXCLUDES else acc + [f], dirs, [])
>>> dirs
['x', 'y']
通过列表
理解,您可以很好地实现这一点,您只需使用空片分配来替换原始目录的内容
:
dirs[:] = [e for e in dirs if e not in EXCLUDES]
# ^^^ Replaces contents ^^^ flip condition so you only keep not-excluded
这不违反任何正确使用listcomp的规则;listcomp没有副作用,它只生成一个新的列表
,然后您可以使用它批量替换目录
的原始内容
除此之外,对于您的问题,“对于每个“语义”,是否有一种(推荐的)方法来运行一个命令”,这是一个for
循环,如您所写:
for x in iterable:
fn(x)
“但是没有什么能阻止你将你写的列表理解分配给dirs本身”实际上,在这种情况下,这不起作用;必须适当修改os.walk
生成的dir
,以影响在下一个循环中行走的内容。将dirs
重新绑定到全新的列表的结果
不会更改隐藏在walk
中的列表
(它在dirs
的初始绑定中提供了别名)。您不能使用dirs=[e for e in dirs if e not in EXCLUDES]
来修改原始列表,但您可以使用切片分配来修改,正如我在回答中所指出的那样。@ShadowRanger您是对的,但问题不太清楚。“更改生成的目录列表以过滤它们”并不完全意味着修剪os.walk
树。我将编辑我的答案。
for x in iterable:
fn(x)