你走在正确的道路上:如果想使用 fnmatch
风格的模式,应该使用带有这种模式的fnmatch.filter
。
但是存在三个问题,使这并不是非常简单。
首先,你想应用多个过滤器。怎么做呢?多次调用 filter
:
for ignore in ignore_files:
filenames = fnmatch.filter(filenames, ignore)
其次,您实际上想做的是filter的相反操作:返回不匹配的名称子集。正如文档所解释:
它与 [n for n in names if fnmatch(n, pattern)]
相同,但实现更有效。
因此,要执行相反操作,您只需添加not
关键字:
for ignore in ignore_files:
filenames = [n for n in filenames if not fnmatch(n, ignore)]
最后,您正在尝试过滤部分路径名而不仅仅是文件名,但在过滤之前还没有进行join
操作。所以请改变顺序:
filenames = [os.path.join(root, filename) for filename in filenames]
for ignore in ignore_files:
filenames = [n for n in filenames if not fnmatch(n, ignore)]
matches.extend(filenames)
有几种方法可以改进这个程序。
你可能会想使用生成器表达式而不是列表推导式(用圆括号代替方括号),这样如果你有大量文件名的列表,就可以使用惰性流水线而不是反复构建巨大的列表来节省时间和空间。
另外,如果你倒置循环的顺序,可能更容易理解,像这样:
filenames = (n for n in filenames
if not any(fnmatch(n, ignore) for ignore in ignore_files))
最后,如果您担心性能问题,您可以使用 fnmatch.translate
处理每个表达式,将它们转换为等效的正则表达式,然后将它们合并成一个大的正则表达式并编译它,而不是在 fnmatch
循环周围进行操作。如果您的模式允许比仅有的 *.jpg
更加复杂,这可能会变得棘手,并且除非您真的确定存在性能瓶颈,否则我不建议这样做。但如果您需要这样做,我曾在SO上看到至少有一个问题,其中某人花费了很多精力解决所有边缘情况,因此请搜索而不是尝试自己编写。
fnmatch.translate
将它们转换为正则表达式,但你有没有任何理由相信你可能需要这样做呢?如果有的话,那个原因应该在你的问题中提到。 - abarnert