剖析一行(混淆的?)Python

Posted

技术标签:

【中文标题】剖析一行(混淆的?)Python【英文标题】:Dissecting a line of (obfuscated?) Python 【发布时间】:2010-08-24 17:20:34 【问题描述】:

我正在阅读关于 Stack Overflow (Zen of 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 pbzcy***grq.\nSyng vf orggre guna arfgrq.\nFcnefr vf orggre guna qrafr.\nErnqnovyvgl pbhagf.\nFcrpvny pnfrf nera'g fcrpvny rabhtu gb oernx gur ehyrf.\nNygubhtu cenpg***yvgl 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 的代码中从未发生过),“或”语句将执行恰好在其右侧的任何内容,在本例中为循环 c for c in this.s

我对那个流程是否正确?

即使我对执行顺序的看法是正确的,这仍然给我留下了很多问题。为什么 是第一个执行的,即使它的代码在几个条件语句之后出现在最后一行?换句话说,为什么for 循环开始执行并赋值,但实际上只有在代码执行的后期才返回一个值,如果有的话?

另外,对于奖励积分,Zen 文件中关于荷兰语的奇怪行是什么?

编辑:虽然现在说出来让我很丢脸,但直到三秒钟前,我还以为 Guido van Rossum 是意大利人。在阅读了他的 Wikipedia 文章后,我至少可以理解(如果不是完全理解的话)为什么会有那行。

【问题讨论】:

啊,this - 一个很好的指导方针,可以作为“不要这样做”的例子 ;) 将其称为混淆了所有实际目的。 【参考方案1】:

列表理解行中的运算符如下关联:

"".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)

删除用于模拟if-else 语句的and/or 布尔技巧:

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

【讨论】:

具体来说,在 Python 中,True and x 的计算结果为 x,而 False and x 的计算结果为 False @Tony Veijalainen:修复了这个问题。 或更短:"".join(this.d.get(c, c) for c in this.s)【参考方案2】:

您对流程的看法是正确的。

循环类似于[dosomething(c) for c in this.s],它是一个列表推导式,应该被视为this.s 中所有c 的dsomething。

荷兰语部分是关于 Python 的 Guido Van Rossum 的创造者是荷兰人。

【讨论】:

【参考方案3】:

您的分析很接近。这是一个列表理解。 (顺便说一句,如果去掉外面的方括号,也会产生相同的输出,这将被称为生成器理解)

有一些文档here。

列表推导的基本形式是

[expression for var in enumerable if condition]

它们按以下顺序评估:

    可枚举的评估 每个值依次分配给 var 条件已检查 表达式被评估

结果是条件为真的枚举中每个元素的表达式值列表。

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

[(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 相同的功能。

总而言之,我根本不会将其称为混淆。一旦您了解了列表推导式的强大功能,它就会看起来很自然。

【讨论】:

All in all, I would not call this obfuscated at all. 你知道,我在四年前问过这个问题,我得说你是对的,我觉得它不再被混淆了【参考方案4】:

通常,列表推导具有以下形式:

[ expression for var in iterator ]

当我写下列表理解时,我通常从写作开始

[ for var in iterator ]

因为多年的过程式编程已经将 for-loop 方面作为第一部分灌输到我的脑海中。

而且,正如您正确指出的,for 循环是似乎首先“执行”的部分。

对于每一次循环,都会计算表达式。 (小点:计算表达式,执行语句。)

所以在这种情况下,我们有

[ expression for c in this.s ]

this.s 是一个字符串。在 Python 中,字符串是迭代器!当你写

for c in some_string:

循环遍历字符串中的字符。所以c 按顺序处理 this.s 中的每个字符。

现在表达式是

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

这就是所谓的ternary operation。该链接解释了逻辑,但基本思想是

if c in this.d:
    the expression evaluates to this.d[c]
else:
    the expression evaluates c

因此,条件c in this.d 只是检查字典this.d 是否有一个值为c 的键。如果是,则返回 this.d[c],如果不是,则返回 c 本身。

另一种写法是

[this.d.get(c,c) for c in this.s]

(get method的第二个参数是第一个参数不在dict中时返回的默认值)。

附言。三元形式

condition and value1 or value2

容易出错。 (考虑如果condition 为真,但value1None 时会发生什么。由于condition 为真,您可能期望三元形式的计算结果为value1,即None。但由于@ 987654343@ 具有布尔值 False,而三元形式的计算结果为 value2。因此,如果您不小心并意识到这个陷阱,三元形式可能会引入错误。)

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

value1 if condition else value2

它不易受上述陷阱的影响。如果 condition 为 True,则表达式的计算结果始终为 value1

但在上述特定情况下,我更喜欢this.d.get(c,c)

【讨论】:

这是什么? this.d.get[c,' '] @recursive: this.get[c, ''] 是一个语法错误。也许你的意思是this.get(c, '')。在交互式解释器中尝试help(.get) @John:不,我是认真的。它不再有意义,因为答案已被编辑。【参考方案5】:

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

this.s.decode('rot13')

【讨论】:

【参考方案6】:

我的带有现代 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.s 要解扰消息,请将 dict this.d 中的那些字母替换为其解码的对应部分(大写/小写不同)。其他字母无需更改,按原样打印。

【讨论】:

以上是关于剖析一行(混淆的?)Python的主要内容,如果未能解决你的问题,请参考以下文章

Android内外存储 易混淆点剖析(/mnt/sdcard/storage/sdcard0/storage/emulated/0等区别)

Android内外存储 易混淆点剖析(/mnt/sdcard/storage/sdcard0/storage/emulated/0等区别)

深度剖析 | JVM深层系列[HotSpotVM研究系列] JVM调优的"标准参数"的各种陷阱和坑点分析(攻克盲点及混淆点)「 1 」

深度剖析 | JVM深层系列[HotSpotVM研究系列] JVM调优的“标准参数“的各种陷阱和坑点分析(攻克盲点及混淆点)「 1 」

Android Dex VS Class:实例图解剖析两种格式文件结构优劣

Android Dex VS Class:实例图解剖析两种格式文件结构优劣