tel tel - 1 year ago 127
Python Question

Dissecting a line of (obfuscated?) Python

I was reading another question on Stack Overflow (Zen of Python), and I came across this line in Jaime Soriano's answer:

import this
"".join([c in this.d and this.d[c] or c for c in this.s])

Entering the above in a Python shell prints:

"The Zen of Python, by Tim Peters\n\nBeautiful is better than ugly.\nExplicit is
better than implicit.\nSimple is better than complex.\nComplex is better than
complicated.\nFlat is better than nested.\nSparse is better than dense.
\nReadability counts.\nSpecial cases aren't special enough to break the rules.
\nAlthough practicality beats purity.\nErrors should never pass silently.
\nUnless explicitly silenced.\nIn the face of ambiguity, refuse the temptation to
guess.\nThere should be one-- and preferably only one --obvious way to do it.
\nAlthough that way may not be obvious at first unless you're Dutch.\nNow is
better than never.\nAlthough never is often better than *right* now.\nIf the
implementation is hard to explain, it's a bad idea.\nIf the implementation is
easy to explain, it may be a good idea.\nNamespaces are one honking great idea
-- let's do more of those!"

And so of course I was compelled to spend my entire morning trying to understand the above list... comprehension... thing. I hesitate to flatly declare it obfuscated, but only because I've been programming for just a month and a half and so am unsure as to whether or not such constructions are commonplace in python.

contains an encoded version of the above printout:

"Gur Mra bs Clguba, ol Gvz Crgref\n\nOrnhgvshy vf orggre guna htyl.\nRkcyvpvg vf orggre guna vzcyvpvg.\nFvzcyr vf orggre guna pbzcyrk.\nPbzcyrk vf orggre guna pbzcyvpngrq.\nSyng vf orggre guna arfgrq.\nFcnefr vf orggre guna qrafr.\nErnqnovyvgl pbhagf.\nFcrpvny pnfrf nera'g fcrpvny rabhtu gb oernx gur ehyrf.\nNygubhtu cenpgvpnyvgl orngf chevgl.\nReebef fubhyq arire cnff fvyragyl.\nHayrff rkcyvpvgyl fvyraprq.\nVa gur snpr bs nzovthvgl, ershfr gur grzcgngvba gb thrff.\nGurer fubhyq or bar-- naq cersrenoyl bayl bar --boivbhf jnl gb qb vg.\nNygubhtu gung jnl znl abg or boivbhf ng svefg hayrff lbh'er Qhgpu.\nAbj vf orggre guna arire.\nNygubhtu arire vf bsgra orggre guna *evtug* abj.\nVs gur vzcyrzragngvba vf uneq gb rkcynva, vg'f n onq vqrn.\nVs gur vzcyrzragngvba vf rnfl gb rkcynva, vg znl or n tbbq vqrn.\nAnzrfcnprf ner bar ubaxvat terng vqrn -- yrg'f qb zber bs gubfr!"

contains a dictionary with the cypher that decodes

{'A': 'N', 'C': 'P', 'B': 'O', 'E': 'R', 'D': 'Q', 'G': 'T', 'F': 'S', 'I': 'V', 'H': 'U', 'K': 'X', 'J': 'W', 'M': 'Z', 'L': 'Y', 'O': 'B', 'N': 'A', 'Q': 'D', 'P': 'C', 'S': 'F', 'R': 'E', 'U': 'H', 'T': 'G', 'W': 'J', 'V': 'I', 'Y': 'L', 'X': 'K', 'Z': 'M', 'a': 'n', 'c': 'p', 'b': 'o', 'e': 'r', 'd': 'q', 'g': 't', 'f': 's', 'i': 'v', 'h': 'u', 'k': 'x', 'j': 'w', 'm': 'z', 'l': 'y', 'o': 'b', 'n': 'a', 'q': 'd', 'p': 'c', 's': 'f', 'r': 'e', 'u': 'h', 't': 'g', 'w': 'j', 'v': 'i', 'y': 'l', 'x': 'k', 'z': 'm'}

As far as I can tell, the flow of execution in Jaime's code is like this:

1. the loop
c for c in this.s
assigns a value to c

2. if the statement
c in this.d
evaluates to True, the "and" statement executes whatever happens to be to its immediate right, in this case

3. if the statement
c in this.d
evaluates to False (which never happens in Jaime's code), the "or" statement executes whatever happens to be to its immediate right, in this case the loop
c for c in this.s

Am I correct about that flow?

Even if I am correct about the order of execution, this still leaves me with a ton of questions. Why is <1> the first thing to execute, even though the code for it comes last on the line after several conditional statements? In other words, why does the
loop begin to execute and assign value, but then only actually return a value at a later point in the code execution, if at all?

Also, for bonus points, what's with the weird line in the Zen file about the Dutch?

Edit: Though it shames me to say it now, until three seconds ago I assumed Guido van Rossum was Italian. After reading his Wikipedia article, I at least grasp, if not fully understand, why that line is in there.

sth sth
Answer Source

The operators in the list comprehension line associate like this:

"".join([(((c in this.d) and this.d[c]) or c) for c in this.s])

Removing the list comprehension:

result = []
for c in this.s:
   result.append(((c in this.d) and this.d[c]) or c)
print "".join(result)

Removing the and/or boolean trickery, which is used to emulate a if-else statement:

result = []
for c in this.s:
   if c in this.d:
print "".join(result)
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download