开发者

How to do a nested list comprehension without making a product?

开发者 https://www.devze.com 2023-02-20 14:09 出处:网络
In Python, I want to list all files in a set of directories. The best I\'d like to get is a list. But at most I managed to make a nested list:

In Python, I want to list all files in a set of directories. The best I'd like to get is a list. But at most I managed to make a nested list:

pics = os.path.expanduser('~/Pictures')
all_pics = [(d, os.listdir(d)) for d in os.listdir(pics)]

result:

[('folder1', ['file1', 'file2', ...]), ('folder2', ['file1', ...]), ...]

what I want:

[('folder1' 'file1'), ('folder1', 'file2'), ..., (开发者_运维技巧'folder2', 'file1'), ...]

What I would like to get is a simple plain list, doesn't matter of what (can be of tuples), just so that it hasn't nested things, and I need no nested cycles in the code that parses it.

How can I do this with list comprehensions? Doing this gives me a product of 2 sets (dir names and filenames) which is wrong:

[(d, f) for f in os.listdir(os.path.join(pics, d)) for d in os.listdir(pics)]


You got the order of the for-loops wrong. It should be

all_pics = [(d, f)
            for d in os.listdir(pics)
            for f in os.listdir(os.path.join(pics, d))]

Outermost loop first, innermost loop last.

I wonder why you didn't get a NameError for d.


Using os.walk is a better idea:

all_pics = [(dir,file)
            for dir,sub,files in os.walk(pics) 
            for file in files]

So why is it a better idea?

  • it works with deeper trees, too (subdir/subdir/pic.jpg)
  • it doesn't break if you put a file in ~/Pictures (If you call os.listdir on files, you get OSError: [Errno 20] Not a directory)
  • it's simpler: os.walk does the traversing for you, all that remains is formatting the output


List comprehensions work as a mapping from one list to another. As another poster pointed it you can do it by nesting them, but are you sure you really want to? You should favour readability over everything else, in all most all cases - and I don't consider the nested comprehension to be easily understandable.

What is wrong with? It might be marginally slower - but will you notice?

files = []
for d in os.listdir(pics):
    for f in os.listdir(os.path.join(pics, d)):
        files.append((d, f))

If you don't want to generate the list until absolutely necessary you could use a generator:

def get_file_mapping():
    for d in os.listdir(pics):
        for f in os.listdir(os.path.join(pics, d)):
            yield (d, f)
files = list(get_file_mapping())


You simply need to flatten your output list:

pics = os.path.expanduser('~/Pictures')
all_pics = reduce(lambda xs,ys: xs+ys, [(d, os.listdir(d)) for d in os.listdir(pics)])


l = [ [(d, f) for f in os.listdir(os.path.join(pics, d))] for d in os.listdir(pics) ]
l = sum(l, []) # flatten list of lists => list
0

精彩评论

暂无评论...
验证码 换一张
取 消