0

Would you please explain why this Python code works to a C programmer?

This idiom works to retrieve a file list, recursively

[os.path.join(dp, f) for dp, dn, fn in os.walk(os.path.expanduser(dir)) for f in fn]

I found that style here in Recursive os.listdir?. It has no punctuation, or nesting. I can't understand why it works. Can you write same in a more tedious style that I can understand so that I don't feel so helpless? :)

People usually say "give us an example of why you use that", and I'll show you the whole program. I was so stunned it worked at all I blogged about it, with a picture http://pj.freefaculty.org/blog/?p=285

That's for Ubuntu 14.10 with the XFCE4 desktop and the Compiz window manager/compositor. It will replace background wallpapers on the viewports by interacting with Dconf through gsettings.

The usage I don't understand is inside random.choice() below:

#!/usr/bin/env python3

import argparse
import subprocess
import sys
import os
import random

parser = argparse.ArgumentParser()
parser.add_argument("-d", "--dir",  help = "directory path", 
                default = "/usr/local/share/Backgrounds")
parser.add_argument("-w", "--workspace", 
                help = "workspace number, 0, 1-n, or > n", default = "-1", 
                type = int)
parser.add_argument("-schema", help = "gsettings shema", 
                metavar = "SCHEMA", default = "org.compiz.wallpaper:/org/compiz/profiles/Default/plugins/wallpaper/")
parser.add_argument("-key", help = "gsettings key", metavar = "KEY", default = "bg-image")
args = parser.parse_args()

array = eval(subprocess.check_output(["gsettings", "get", args.schema, args.key]))

## print(array)
arraylen = len(array)

filename = random.choice([os.path.join(dp, f) for dp, dn, fn in os.walk(os.path.expanduser(args.dir)) for f in fn])
print("The newly found filename is:")
print(filename)

ws = args.workspace - 1

## If ws 0 or smaller, we are going to reset whole collection back to
## just one image. if ws > N of images, then add a new image.
if ws < 0:
    array=[str(filename)]
    subprocess.call(["gsettings", "set", args.schema, args.key, str(array)])
    subprocess.call(["gsettings", "set", args.schema, "bg-fill-type", str("[0]")])
    subprocess.call(["gsettings", "set", args.schema, "bg-image-pos", str("[0]")])
    subprocess.call(["gsettings", "set", args.schema, "bg-color1", str("['#000000ff']")])
    subprocess.call(["gsettings", "set", args.schema, "bg-color2", str("['#000000ff']")])
elif ws < arraylen:
    array[ws]=str(filename)
    subprocess.call(["gsettings", "set", args.schema, args.key, str(array)])
else:
    array.append(str(filename))
    subprocess.call(["gsettings", "set", args.schema, args.key, str(array)])
    arraylen = len(array)
    subprocess.call(["gsettings", "set", args.schema, "bg-fill-type", str([0]*arraylen)])
    subprocess.call(["gsettings", "set", args.schema, "bg-image-pos", str([0]*arraylen)])
    subprocess.call(["gsettings", "set", args.schema, "bg-color1", str(['#000000ff']*arraylen)])
    subprocess.call(["gsettings", "set", args.schema, "bg-color2", str(['#000000ff']*arraylen)])

subprocess.call(["gsettings", "set", args.schema, args.key, str(array)])

print("HELLO, corrected image array is:")               
print('\n '.join(array))

If you say "we don't understand what you don't understand," I get it. Here are some specific questions I want to ask

  1. Why is there no bracket around dp, dn, fn
  2. The for statement at the end "for f in fn" has no statements "inside it".
  3. How does os.path.join(dp, f) work when dp and f don't exist before that statement?

I get the same allergic feeling in Perl when people put an if statement and the end of a line, rather than at the beginning.

Community
  • 1
  • 1
pauljohn32
  • 2,079
  • 21
  • 28

1 Answers1

0

When you run:

a=[[1,2],[3,4]]
[x for y in a for x in y]

You get:

[1,2,3,4]

So in words, it runs y for every entry in a and than set x as each entry of y, so it is equivalent to:

for y in a:
    for x in y:
        code with x

You can rewrite your code into:

result=[]
for dp, dn, fn in os.walk(os.path.expanduser(args.dir)):
   for f in fn:
       result.append(os.path.join(dp, f))

Important is, that the first one is always the left for cycle so it is possible to use the variables in such way.

As for the brackets, it is possible to write it there, but it is implicitly clear that it must be tuple, so it is not essential to write it.

knezi
  • 247
  • 1
  • 12