剖析一行(混淆的?)Python代码

8 投票
6 回答
1017 浏览
提问于 2025-04-16 03:14

我在阅读Stack Overflow上的另一个问题时,看到Jaime Soriano的回答中有这么一句话:

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

在Python的命令行中输入上面的内容会打印出:

"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!"

所以我当然忍不住花了整个早上去搞懂这个列表...理解...的东西。我不想直接说它很复杂,但只是因为我才编程一个半月,所以不确定这种写法在Python中是否常见。

this.s包含了上面打印内容的编码版本:

"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!"

this.d则包含了一个字典,用来解码this.s

{'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'}

根据我所理解,Jaime的代码执行流程大概是这样的:
1. 循环c for c in this.s给c赋值
2. 如果c in this.d这个判断为真,那么“和”后面的内容会被执行,这里是this.d[c]
3. 如果c in this.d这个判断为假(在Jaime的代码中从未发生),那么“或”后面的内容会被执行,这里是循环c for c in this.s

我对这个执行流程理解得对吗?

即使我对执行顺序理解得没错,这依然让我有很多疑问。为什么<1>是第一个执行的,尽管它的代码在行的最后,前面还有几个条件判断?换句话说,为什么for循环开始执行并赋值,但实际返回值却是在代码执行的后面,甚至可能根本不返回?

另外,作为额外问题,Zen文件中关于荷兰人的那句奇怪的话是怎么回事?

编辑:虽然现在说出来让我有点不好意思,但在三秒钟前我还以为Guido van Rossum是意大利人。看了他的维基百科后,我至少明白了,虽然不完全理解,为什么那句话会在里面。

6 个回答

2

你的分析很接近。其实这是一个列表推导式。(顺便说一下,如果去掉外面的方括号,结果也是一样的,那就叫生成器推导式)

这里有一些文档可以参考 这里

列表推导式的基本形式是

[expression for var in enumerable if condition]

它们的执行顺序是:

  1. 首先计算可遍历的对象
  2. 然后依次把每个值赋给变量
  3. 接着检查条件
  4. 最后计算表达式

结果就是对于每个符合条件的可遍历对象中的元素,得到的表达式值组成的列表。

这个例子没有使用条件,所以在加上一些括号后,剩下的就是:

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

this.s 是可遍历的对象。c 是循环变量。c in this.d and this.d[c] or c 是表达式。

c in this.d and this.d[c] or c 利用 Python 逻辑运算符的短路特性,达到了和 this.d[c] if c in this.d else c 一样的效果。

总的来说,我觉得这并不复杂。一旦你理解了列表推导式的强大,它看起来会非常自然。

2

你说得对,流程是这样的。

这个循环的写法是 [dosomething(c) for c in this.s]。这是一种列表推导式,可以理解为对这个列表中的每一个c都执行一次dosomething。

提到荷兰,是因为Python的创始人Guido Van Rossum是荷兰人。

11

列表推导式中的操作符是这样关联的:

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

去掉列表推导式:

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

去掉 and/or 的布尔技巧,这个技巧是用来模拟 if-else 语句的:

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

撰写回答