我设法将8行代码转换为2行。
第一个列表理解获取文件夹,第二个获取特定过滤器的文件:
hideTheseFolders=[".thumb",".mayaSwatches","RECYCLER","$AVG"]
fileFilters=["ma","jpg","png","mb",'iff','tga','tif']
newLst=[]
import os
locationTxt="E:\box\scripts"
[newLst.append(each) for each in os.listdir(locationTxt) if os.path.isdir(os.path.join(locationTxt,each)) and each not in hideTheseFolders]
[newLst.append(os.path.basename(os.path.join(locationTxt,each))) for nfile in fileFilters for each in os.listdir(locationTxt) if each.endswith(nfile)]
现在在上面的代码中,最后两行是从locationTxt
查看同一目录,这意味着可能有一种方法可以合并最后两行。有什么建议?
列表推导不是优化技术。当Python编译器看到列表推导时,它会将其分解为for循环。看一下字节码13(FOR_ITER
):
In [1]: from dis import dis
In [2]: code = "[i for i in xrange(100)]"
In [3]: dis(compile(code, '', 'single'))
1 0 BUILD_LIST 0
3 LOAD_NAME 0 (xrange)
6 LOAD_CONST 0 (100)
9 CALL_FUNCTION 1
12 GET_ITER
>> 13 FOR_ITER 12 (to 28)
16 STORE_NAME 1 (i)
19 LOAD_NAME 1 (i)
22 LIST_APPEND 2
25 JUMP_ABSOLUTE 13
>> 28 POP_TOP
29 LOAD_CONST 1 (None)
32 RETURN_VALUE
列表理解与for循环相同的事实也可以通过计时来看出。在这种情况下,for循环实际上稍微(但微不足道)加快了:
In [4]: %timeit l = [i for i in xrange(100)]
100000 loops, best of 3: 13.6 us per loop
In [5]: %%timeit l = []; app = l.append # optimise out the attribute lookup for a fairer test
...: for i in xrange(100):
...: app(i)
...:
100000 loops, best of 3: 11.9 us per loop # insignificant difference. Run it yourself and you might get it the other way around
因此,您可以将任何给定的列表推导编写为具有最小性能命中的for循环(实际上,由于属性查找通常存在很小的差异),并且通常具有显着的可读性益处。特别是,不应将具有副作用的循环写为列表推导。您也不应该使用包含超过两个for
关键字的列表推导,或者使一行超过70个字符左右的列表推导。这些不是硬性规则,只是用于编写可读代码的启发式方法。
不要误解我的意思,列表推导非常有用,并且通常比等效的for-loop-and-append更清晰,更简单,更简洁。但他们不应该以这种方式被滥用。
首先,你滥用列表推导来通过追加内部来隐藏循环;你实际上扔掉了列表理解的结果。其次,没有必要以牺牲可读性为代价尽可能多地填充到一行中。
如果你想使用列表推导,这在通过循环和过滤构建列表时实际上是个好主意,那么请考虑以下版本:
ignore_dirs = set([".thumb",".mayaSwatches","RECYCLER","$AVG"])
extensions = ["ma", "jpg", "png", "mb", 'iff', 'tga', 'tif']
location = "E:\\box\\scripts"
filelist = [fname for fname in os.listdir(location)
if fname not in ignore_dirs
if os.path.isdir(os.path.join(location, fname))]
filelist += [os.path.basename(fname)
for fname in os.listdir(location)
if any(fname.endswith(ext) for ext in extensions)]
请注意,仍然有两种理解,因为您似乎正在构建一个逻辑上由两种项组成的列表。没有必要尝试在单个表达式中执行此操作,尽管您可以在它们之间使用+
而不是+=
语句使用两种理解。
(我冒昧地重命名变量以反映它们所代表的内容。)
主要的建议是获得一本体面的Python书并阅读它。从你的代码判断你不知道列表推导是如何工作的,你仍然设法将8行可读代码塞进2个过长且难以理解的代码中。
你应该编写易于阅读的程序:
imports
放在文件的开头万一你想知道,这是你的代码应该是什么样子:
import os
path = 'e:/box/scripts'
newLst = list()
for root,dirs,files in os.walk(path) :
# add folders
newLst.extend( [dir for dir in dirs if dir not in hideTheseFolders] )
# add files
newLst.extend( [file for file in files if file.lower().endswith(fileFilters)] )
break # don't descend into subfolders
# convert to the full path or whatever you need here
newLst = [os.path.join(path, file) for file in newLst]
我会坚持使用更易读的代码并避免列表理解,或者如果我必须做列表理解,我会保留对可读的备份引用。
到目前为止,我的学习列表理解我会把它放在每个人都可以跟随。
理解的主要用途是:
files = [f for f in list_files() if f.endswth("mb")]
example = "abcde"; letters = [x for x in example] # this is handy for data packed into strings!
strings = [str(x) for x in list_of_numbers]
filter_func = lambda p, q: p > q
larger_than_last = [val for val in list_of_numbers if filter_func(val, 5)]
感谢大家的投入和指导。
更新:我的研究和故障排除让我得到了确切的答案。
filters = [[".thumb", ".mayaSwatches", "RECYCLER", "$AVG"], ["ma", "jpg", "png", "mb", 'iff', 'tga', 'tif']]
locationTxt = r"E:\box\scripts"
newLst = [each for each in os.listdir(locationTxt) if os.path.isdir(os.path.join(locationTxt, each)) and each not in filters[0]] + [each for each in os.listdir(locationTxt) if os.path.isfile(os.path.join(locationTxt, each)) and os.path.splitext(each)[-1][1:] in filters[1]]
但是,正如我所提到的,坚持可读的代码逻辑是要走的路!