我想完全控制基于os.walk的列表理解(后者将变成生成器表达式),但找不到方法。我是否达到了Python表达能力的极限,还是我缺乏的技巧词汇? : - )
这就是我所拥有的,甚至为此我不得不破解(短路逻辑表达式并强制为True),因为我可以找到一种方法来使用赋值运算符注入任何东西。无论我多么努力埋葬它,解析器都会找到我:-)也无法在任何地方注入打印(作为调试辅助工具)。
[( s, re.sub(r"^.*?FStar\.(.*)\.fs(.*)", dest_dir + r"Fstar.\1.fst\2", s) )
for s in ( x[0].replace('\\', '/') + "/" + f
for x in os.walk(".")
if (skip_dir in x[1] and x[1].remove(skip_dir) or True)
for f in x[2] if fnmatch(f ,patt))
]
我想要的是完全控制dirs:
x[1][:]=[d for d in x[1] if d not in skip_list]
这让我想到了Python的表现力的极限。
对于这个特定案例的任何想法都表示赞赏,但是如果有人知道/有一个构造的例子提供比os.walk更多的潜在控制,只是面对理解嵌套/组合的限制。
在写这篇文章时,我也开始希望使用管道或其他形式的发电机组合器。
理解结构(通常在句法结构中)表达能力的最终限制是一个更大的问题。
对于那些要求澄清的人 - 第一个问题是如何在理解中实现对os.walk的控制,能够删除多个项目(来自skip_list)而不仅仅是一个,第二个是限制是什么Pythons表达能力 - 还有什么,可以在嵌套的理解结构中完成。例如,作业。
答案 0 :(得分:1)
所有 Python 用户都应该熟悉Zen。
在这种情况下,我可以看到违反下一条规则
可读性计数。
给出的例子太难以理解了。
简单比复杂更好。
稀疏优于密集。
在一次理解中有太多陈述。
Flat比嵌套好。
我们在这里有嵌套的理解。
理解中的副作用不是一个好主意
x[1].remove(skip_dir)
参见讨论here
而不是字符串连接
x[0].replace('\\', '/') + "/" + f
我们使用os.path.join
os.path.join(x[0].replace('\\', '/'), f)
关于声明
for x in os.walk(".")
os.walk
产生具有根目录,子目录和文件名称的元组,因此最好解包tuple而不是按索引访问坐标
for root, directories_names, files_names in os.walk('.')
如果我们多次使用相同的正则表达式,那么在使用之前它应该被编译,所以
... re.sub(r"^.*?FStar\.(.*)\.fs(.*)", dest_dir + r"Fstar.\1.fst\2", s) ...
可分为
TARGET_FILES_NAMES_RE = re.compile(r"^.*?FStar\.(.*)\.fs(.*)")
... TARGET_FILES_NAMES_RE.sub(dest_dir + r"Fstar.\1.fst\2", s) ...
也应该忽略dest_dir + r"Fstar.\1.fst\2"
应该做什么:我相信它应该将dest_dir
加入到简化的文件名中。
当理解变得复杂(或特别复杂)时,将其重写为生成函数是个好主意。
对于给定的例子,它可能像
TARGET_FILES_NAMES_RE = re.compile(r"^.*?FStar\.(.*)\.fs(.*)")
def modify_paths(top, dest_dir, skip_dir, pattern):
replacement = os.path.join(dest_dir, r"Fstar.\1.fst\2")
for root, directories_names, files_names in os.walk(top):
try:
# we are removing `skip_dir` from all subdirectories,
# is it a desired behavior?
directories_names.remove(skip_dir)
except ValueError:
# not in list
pass
for file_name in files_names:
if not fnmatch(file_name, pattern):
continue
s = os.path.join(root.replace('\\', '/'), file_name)
yield (s,
TARGET_FILES_NAMES_RE.sub(replacement, s))
但它仍然是原始的,应该重构。
我已经创建了目录
/
doc.txt
FStar.anotherfs
FStar.other.fS
FStar.some.fs90
FStar.text..fs
test.py
skip_me/
FStar.file.fs
FStar.sample.fs
FStar.skipped.fs
其中test.py
内容:
import os
import re
from fnmatch import fnmatch
TARGET_FILES_NAMES_RE = re.compile(r"^.*?FStar\.(.*)\.fs(.*)")
def modify_paths(top, dest_dir, skip_dir, pattern):
replacement = os.path.join(dest_dir, r"Fstar.\1.fst\2")
for root, directories_names, files_names in os.walk(top):
try:
# we are removing `skip_dir` from all subdirectories,
# is it a desired behavior?
directories_names.remove(skip_dir)
except ValueError:
# not in list
pass
for file_name in files_names:
if not fnmatch(file_name, pattern):
continue
s = os.path.join(root.replace('\\', '/'), file_name)
yield (s,
TARGET_FILES_NAMES_RE.sub(replacement, s))
if __name__ == '__main__':
top = '.'
skip_dir = 'skip_me'
patt = '*'
# slash is required for version with list comprehension
dest_dir = 'dest_dir/'
before = [
(s,
re.sub(r"^.*?FStar\.(.*)\.fs(.*)", dest_dir + r"Fstar.\1.fst\2", s))
for s in (os.path.join(x[0].replace('\\', '/'), f)
for x in os.walk(top)
if (skip_dir in x[1] and
x[1].remove(skip_dir) or True)
for f in x[2] if fnmatch(f, patt))]
after = list(
modify_paths(top=top, dest_dir=dest_dir, skip_dir=skip_dir,
pattern=patt))
assert after == before
并断言成功。
跳过异常并不是一个好主意,但如果你知道会发生什么,它可以成为一个强大的工具。
也可以使用
中的contextlib.suppress
上下文管理器重写它
try:
directories_names.remove(skip_dir)
except ValueError:
pass
到
with suppress(ValueError):
directories_names.remove(skip_dir)