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)