お前らのZen of Pythonの捉え方は間違っている

↑初めての記事だし、こういう煽るタイトルをつけてみたかった。後悔はしていない。
zen of pythonってググって上位に出るのがクソ真面目なリンクばっかりだったから書こうと思った話。

## Zen of Pythonとは？

この記事がすごく良く和訳している。(というかググったら最初に来る)

https://qiita.com/IshitaTakeshi/items/e4145921c8dbf7ba57ef

## で、実際はどうなの？

Zen of Pythonが極めて有用なPythonプログラムを書く上での指針で有ることは間違いないが、本質的にはジョークで"も"ある。
所々に矛盾が仕込まれており、"Python"の名前の由来[^1]に恥じないイースターエッグ[^2]である。

以下に具体例を示す。
なお、和訳は非常に良く訳されている記事[^3]から引用しています。作者の@IshitaTakeshiさんからの削除要請が来たら該当部分は消します。
また、具体例は(意図せずだが)ぶっちゃけ殆ど[この記事](https://dev.to/abdurrahmaanj/the-zen-of-python-is-a-joke-and-here-is-why-you-should-not-take-it-too-seriously-508d)の和訳なので、英語が苦にならない諸君はすぐにそっちを読むべきである。そっちの方がずっと面白おかしく書いているし。あと作者の和訳能力不足で書けなかった内容もあるし。

[^1]: "Monty Python’s Flying Circus"という有名なイギリスのコメディ番組。邦題は「空飛ぶモンティ・パイソン」。
[^2]: [wikipedia](https://ja.wikipedia.org/wiki/イースター・エッグ_(コンピュータ))
[^3]: [プログラマが持つべき心構え (The Zen of Python)](https://qiita.com/IshitaTakeshi/items/e4145921c8dbf7ba57ef)

### `>>> import this`

Zen of Python を表示させる為のコマンドだが、早速矛盾している。

> Explicit is better than implicit.
> 暗示するより明示するほうがいい。

`import`しただけなのに`print`される

### There should be one-- and preferably only one --obvious way to do it.

これは[^3]では`何かいいやり方があるはずだ。誰が見ても明らかな、たったひとつのやり方が。`と訳されているが、[^3]の感想欄での@akinomyogaさんの案を採用させて頂く。(このほうが恐らく原文に近いので)

> There should be one-- and preferably only one --obvious way to do it.
> 何かを実現するのに、一つは (そして望むらくはただ一つの) 明白な方法を用意するべき

この一文だけで矛盾している。
至極最もな事を言っているが、`one-- `と` --obvious`で`--`の使い方が二通りある。[^4]

[^4]: なお19行目の`Namespaces are one honking great idea -- let's do more of those!`を加えると3通りである。それともこれすら作者のTim Petersはオランダ人(Guido van Rossum)じゃないから何通りもの使い方をしていまう、というジョークなのだろうか？

### `this`モジュールの中身

Pythonでは`inspect`モジュールを使用することで簡単に(Pythonで実装された部分の)ソースコードが覗ける。

```Python
>>> import inspect
>>> print(inspect.getsource(this))
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]))
```

ファ！？

解説すると、`s`はROT-13[^5]で暗号化されたZen of Pythonの本文であり、続く内容はROT-13の暗号化・復号化のコードである。

> Beautiful is better than ugly.
> 醜いより美しいほうがいい。
> Readability counts.
> 読みやすいことは善である。

どう考えても美しくないし、読みやすくはない。変数名`s`や`d`、`c`も明示的ではない。
さらに言えば、Pythonには既に標準モジュールの`codecs`ROT-13の暗号化・復号化関数が用意されている。[^6]

> Simple is better than complex.
> 複雑であるよりは平易であるほうがいい。

どう考えてももう一度実装するよりかは標準モジュールの関数を使用したほうが平易だ。[^7]

[^5]: すごく簡単な、それでいて面白い特性を持つ暗号化方式。なお暗号強度はジョークレベル。[wikipedia](https://ja.wikipedia.org/wiki/ROT13)
[^6]: `codecs.decode(string_to_decode_or_encode, "rot13")`
[^7]: この[Stackoverflowの回答](https://stackoverflow.com/a/50664329)にあるが、一回[簡略化](https://github.com/python/cpython/commit/d310f13cd2c321f0cca8429f611546c7888ae561)された後にまた元に[戻されて](https://github.com/python/cpython/commit/0a83778572276c2ed0c7d3184508a4a1635c2f94)いる。

### 公式見解

python.orgには[ユーモア](https://www.python.org/doc/humor/)のリンクがある。
その最初の項目が[The Zen of Python](https://www.python.org/doc/humor/#the-zen-of-python)である。

## 後書きと言い訳

作者の文才不足でこんな「なぜこのジョークは面白いのか」と真面目に解説する様な記事が出来て本当に申し訳ない。
また、原作者の本来の意図を読み解きたい奇特な人達は是非PEP[^8]20[^9]にある[メーリングリストの履歴](https://groups.google.com/g/comp.lang.python/c/B_VxeTBClM0/m/L8W9KlsiriUJ)を読んでもらいたい。
多分原作者のTim Petersは真面目くさった記事ばかりの現状を見てこう思っているだろう:

> **こんなげーむにまし゛になっちゃってと゛うするの**

なお、作者はあるStackoverflowの質問で初めてこれがジョークでもある事を知った。
残念ながらそのスレッドは見つからなかったが、そこではZen of Pythonを根拠に出した回答に対して"People take it too seriously"(みんな真面目に捉えすぎだ)というコメントがあったのを覚えている。
そして気になってipythonで`import this`した後中身が気になり`this.`を入力し2回タブを押した所…といった所である。

[^8]: "Python Enhancement Proposal"の略。雑に言えばPythonに関する正式な提案書。有名な例で言えばPythonコードのコーディング規約のPEP8だろうか。少し踏み込んだ内容を知るのに非常に役に立つので、読んで損は無い。
[^9]: https://www.python.org/dev/peps/pep-0020/

## おまけ

### 19行？20行？

Zen of Pythonが書かれたPEP20[^9]を見ると、Abstractはこの様に書かれている:

> Long time Pythoneer Tim Peters succinctly channels the BDFL's guiding principles for Python's design into 20 aphorisms, only 19 of which have been written down.

雑に和訳する[^10]と:

> 長年のPythoneerであるTim Petersは、BDFL[^11]のPython設計の指針を20の格言に簡潔にまとめていますが、そのうち19のみが書き記されました。

となる。"References"にある[リンク](https://groups.google.com/g/comp.lang.python/c/B_VxeTBClM0/m/L8W9KlsiriUJ)を辿ると、20行目の内容はGuidoに任されているらしい。[^12]

[^10]: DeepLにかけて軽く手直ししただけとも言う。
[^11]: Benevolent Dictator For Lifeの略、Guido van Rossum(Pythonの生みの親)の意。
[^12]: ちなみに作者は「ユーモアを忘れるな」説を押したい。最もこれを書いている時点でユーモアに欠けているのだが。

### `d.get(c, c)`の説明

歴戦のPythonistaには自明かもしれないが、作者のようなニワカには仕組みが良く分からなかったので、以下説明する。

問題のコードは以下の通りである。

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

ここで、`d`はROT-13の暗号・復号対応表の`dict`であり、`s`はROT-13で暗号化された`str`である。
`dict`の`get()`はこの[Stackoverflowの回答](https://stackoverflow.com/a/11041421)が良く説明している。

```python
try:
    value = dictionary[some_key]
except KeyError:
    value = default_value
# 上のコードは次のコードと等価
value = dictionary.get(some_key, default_value)
```

つまり、すごく丁寧に書き直すと、以下の様になる:

```python
encoded_string = s
decoding_dict = d

decoded_list_of_chars = []
for encoded_char in encoded_string:
    try:
        decoded_char = decoding_dict[encoded_char]
    except KeyError:
        decoded_char = encoded_char
    decoded_list_of_chars.append(decoded_char)

decoded_string = "".join(decoded_list_of_chars)
print(decoded_string)
```

### `this.c`の値

実は、python2[^13]とpython3でthis.cの値が違う。

```python
# python2
>>> this.c
'!'
### python3
>>> this.c
97
```

この[Stackoverflowの回答](https://stackoverflow.com/a/19848168)にある様に、python2と3ではリストの内包表記で微妙に挙動が違う。
雑に説明するとpython2ではリスト内包表記でスコープが変わらない為、この様に同じ名前を使うと前の値を上書きしてしまう。
上記の回答から少し流用してサンプルコードを書くと:

```python
>>> x = "before"
>>> a = [x for x in 1, 2, 3]
>>> print(a)
# python2
3
# python3
'before'
```

python2では内包表記の方が速い現象の一因だと思われる。

[^13]: [python2は2020/1/1を持ってEOLとなっており、未だに使っている人はとっととpython3に移行するべきである。](https://www.python.org/doc/sunset-python-2/)