Python之禅源码阅读

Python luoluolzb 浏览1288次

我们知道,在 IDLE 中输入 import this 就会输出 Tim Peters 的 Python 之禅:

>>> import this
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

翻译如下(来源于网络,内容值得仔细阅读一下):

Python之禅 by Tim Peters

优美胜于丑陋(Python 以编写优美的代码为目标)
明了胜于晦涩(优美的代码应当是明了的,命名规范,风格相似)
简洁胜于复杂(优美的代码应当是简洁的,不要有复杂的内部实现)
复杂胜于凌乱(如果复杂不可避免,那代码间也不能有难懂的关系,要保持接口简洁)
扁平胜于嵌套(优美的代码应当是扁平的,不能有太多的嵌套)
间隔胜于紧凑(优美的代码有适当的间隔,不要奢望一行代码解决问题)
可读性很重要(优美的代码是可读的)
即便假借特例的实用性之名,也不可违背这些规则(这些规则至高无上)

不要包容所有错误,除非你确定需要这样做(精准地捕获异常,不写 except:pass 风格的代码)

当存在多种可能,不要尝试去猜测
而是尽量找一种,最好是唯一一种明显的解决方案(如果不确定,就用穷举法)
虽然这并不容易,因为你不是 Python 之父(这里的 Dutch 是指 Guido )

做也许好过不做,但不假思索就动手还不如不做(动手之前要细思量)

如果你无法向人描述你的方案,那肯定不是一个好方案;反之亦然(方案测评标准)

命名空间是一种绝妙的理念,我们应当多加利用(倡导与号召)

这次在这里解析一下 Python 之禅(this模块)的源代码讲解。先上源代码(在Python安装目录下的Lib\this.py):

s = """Gur Mra bs Clguba, ol Gvz Crgref

Ornhgvshy vf orggre guna htyl.
Rkcyvpvg vf orggre guna vzcyvpvg.
Fvzcyr vf orggre guna pbzcyrk.
Pbzcyrk vf orggre guna pbzcyvpngrq.
Syng vf orggre guna arfgrq.
Fcnefr vf orggre guna qrafr.
Ernqnovyvgl pbhagf.
Fcrpvny pnfrf nera'g fcrpvny rabhtu gb oernx gur ehyrf.
Nygubhtu cenpgvpnyvgl orngf chevgl.
Reebef fubhyq arire cnff fvyragyl.
Hayrff rkcyvpvgyl fvyraprq.
Va gur snpr bs nzovthvgl, ershfr gur grzcgngvba gb thrff.
Gurer fubhyq or bar-- naq cersrenoyl bayl bar --boivbhf jnl gb qb vg.
Nygubhtu gung jnl znl abg or boivbhf ng svefg hayrff lbh'er Qhgpu.
Abj vf orggre guna arire.
Nygubhtu arire vf bsgra orggre guna *evtug* abj.
Vs gur vzcyrzragngvba vf uneq gb rkcynva, vg'f n onq vqrn.
Vs gur vzcyrzragngvba vf rnfl gb rkcynva, vg znl or n tbbq vqrn.
Anzrfcnprf ner bar ubaxvat terng vqrn -- yrg'f qb zber bs gubfr!"""

d = {}
for c in (65, 97):
    for i in range(26):
        d[chr(i+c)] = chr((i+13) % 26 + c)

print("".join([d.get(c, c) for c in s]))

我们可以看到并不是直接输出 Python 之禅的,使用了简单的移位加密算法:

alpha = (alpha + 13) % 26

简单来说就是将一个字母使用它向后移动13位(超过第26个则移到开头,循环移位)的字母代替它,并且大小写保存不变。

并且这个算法是可逆的,也就是说:将替换后的字符再次使用这个算法即可得到原来的字符。因此,你可以将 this.py 运行后输出的内容替换字符串s,运行后会得到上面的串。

ok,现在我们一起来解析源代码。s 是使用移位加密算法计算后的文本,非字母的字符保持不变。

先看 24~26 这 4 行:

d = {}
for c in (65, 97):
    for i in range(26):
        d[chr(i+c)] = chr((i+13) % 26 + c)

首先我们需要知道 chr(d) 函数,它将整数 d 根据 ASCII 码表转换为它对应的字符,如果不知道整数65和97对应的字符是多少。我们可以在 IDLE 试一下:

>>> chr(65)
'A'
>>> chr(97)
'a'

据此我们可以知道这4行代码的作用:使用循环移位加密算法计算 A~Z,a~z 共52个字母得到的值,生成一个字符映射表。可单独运行这4行代码,并输出d,验证一下结果(下面使用 print(json.dumps(d, indent=4)) 输出,需要先 import json):

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

随便找一个字母 t, 从上表可以看到 t-> g, g->t, 数一下t和g分别循环移动13位得到g和t,结果正确。

再看最后一行:

print("".join([d.get(c, c) for c in s]))

刚接触 Python 的同学可能看不懂,这里有3个知识点:

  • str.join(iterable)

    返回一个由 iterable 中的字符串拼接而成的字符串。

  • dict.get(key[, default])

    如果 key 存在于字典中则返回 key 的值,否则返回 default。 如果 default 未给出则默认为 None。

  • [d.get(c, c) for c in s] 等价于下面的代码运行后得到的 l

    l = []
    for c in s:
      l.append(d.get(c, c))
    

    这种语法叫 “列表推导式”, 用来快速创建列表。详细信息参考 Python 文档:列表推导式

所以最后一行代码的作用是:逐个将字符串s中的字符根据映射表替换,然后输出。

Python之禅(this.py)的源代码讲解完毕,You GET 到了吗?

本文标签: Python

版权声明:本文为作者原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://luoluolzb.cn/articles/100/python-zen-source-reading
您需要登录后才发布评论。 点此登录
用户评论 (0条)

暂无评论,赶紧发表一下你的看法吧。