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

8

我正在阅读 Stack Overflow 上的另一个问题(Python 之禅),在 Jaime Soriano 的回答中,我遇到了这一行:

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

在Python shell中输入以上内容会打印出:
"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为True,则“and”语句执行其右侧的任何内容,在本例中为this.d[c]
3.如果语句c in this.d为False(在Jaime的代码中从未发生过),则“or”语句执行其右侧的任何内容,在本例中为循环c for c in this.s
我对执行顺序正确吗?
即使我关于执行顺序是正确的,这仍然让我有很多问题。为什么<1>是要执行的第一件事,即使它的代码出现在几个条件语句之后?换句话说,为什么for循环开始执行并分配值,但只在代码执行的后期(如果有的话)才实际返回值?
此外,作为额外的奖励问题,关于Zen文件中荷兰人的奇怪行是什么意思? 编辑:虽然现在说出来让我感到耻辱,但三秒钟前我还以为Guido van Rossum是意大利人。阅读了他的维基百科文章后,我至少明白了为什么Zen文件中有那一行。

啊,this - 一个很好的指导方针,可以作为一个“不要这样做”的例子。 ;) - user395760
我会称其为实际上混淆的代码。 - Paul Nathan
6个回答

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)

2
具体来说,在Python中, True and x 的结果为 x,而 False and x 的结果为 False - Etaoin
@Tony Veijalainen:已经解决了。 - sth
或者更短:"".join(this.d.get(c, c) for c in this.s) - juliomalegria

2
你的分析很接近。这是一个列表推导式(list comprehension)。(顺便说一下,如果去掉外层方括号,就会得到相同的输出,这被称为生成器推导式(generator comprehension))。
这里有一些文档
列表推导式的基本形式如下:
[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 相同的功能。

总的来说,我不会认为这是混淆的代码。一旦你理解了列表推导的强大之处,它看起来就很自然了。


1
总的来说,我一点也不认为这是混淆的。你知道吗,我四年前问过这个问题,我必须得说你是对的,现在它对我来说看起来并不混淆。 - tel

2
一般来说,列表推导式的形式如下:
[ expression for var in iterator ]

当我写列表推导式时,我经常从写下以下内容开始。
[ for var in iterator ]

因为多年的过程式编程已经将for循环方面深深地烙印在我的脑海中,作为首要部分。
而且,正如您所指出的那样,for循环是似乎“执行”的第一部分。
对于每次循环通过,表达式都会被评估。 (一个小点:表达式被评估,语句被执行。)
所以在这种情况下,我们有
[ expression for c in this.s ]

这是一个字符串。在Python中,字符串是可迭代对象!当你写下以下代码:
for c in some_string:

该循环遍历字符串中的字符。因此,变量 c 按顺序取出该字符串中的每个字符。

现在表达式为

c in this.d and this.d[c] or c

这就是所谓的三元运算符。链接中解释了其逻辑,但基本思想是:
if c in this.d:
    the expression evaluates to this.d[c]
else:
    the expression evaluates c

这段文字的翻译如下:
条件的意思是检查字典是否有一个键值为的键。如果有,返回;如果没有,则返回本身。
另一种写法是:
[this.d.get(c,c) for c in this.s]

get方法的第二个参数是在字典中没有找到第一个参数时返回的默认值。”
“PS. 这是三元表达式形式。”
condition and value1 or value2

“容易出错。 (考虑如果condition为True,但是value1None会发生什么。由于condition为True,您可能希望三元形式求值为value1,也就是None。但是由于None的布尔值为False,因此三元形式实际上会评估为value2。因此,如果您不小心或不了解这个陷阱,三元形式可能会引入错误。)

对于Python的现代版本,更好的编写方式是


value1 if condition else value2

它不容易受到上述陷阱的影响。如果condition为真,表达式总是评估为value1
但在上面的特定情况中,我更喜欢this.d.get(c,c)

这是什么?this.d.get[c,' '] - recursive
@recursive: this.get[c, ''] 是语法错误。也许你的意思是 this.get(c, '')。在交互式解释器中尝试 help({}.get) - John Machin
@John:不,我的意思就是我说的那样。只是现在不再有意义了,因为答案被编辑过了。 - recursive

2

您对流程的理解是正确的。

循环的类型是 [dosomething(c) for c in this.s],它是一个列表推导式,应该被理解为对于 this.s 中的所有 c 进行 dosomething 操作。

荷兰部分是关于 Python 创始人 Guido Van Rossum 是荷兰人的。


2

"".join([c in this.d and this.d[c] or c for c in this.s])肯定是混淆的。这是一个禅版本:

this.s.decode('rot13')


0

我的版本使用了现代的if else和生成器:

import this ## prints zenofpython
print '-'*70
whatiszenofpython = "".join(this.d[c] if c in this.d else c for c in this.s)
zen = ''
for c in this.s:
    zen += this.d[c] if c in this.d else c
print zen

口语版本: 导入this,它的主程序解密并打印消息this.s。 为了解密消息,将在字典this.d中找到的那些字母替换为它们的解码对应项(大小写不同)。其他字母不需要更改,但要按原样打印。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接