Courses

Zen of Python とは?
Zen of Python は、Python の設計における指針となる 19 の格言から成るものです。当初は 20 の格言になる予定でしたが、Python の創始者である Guido van Rossum は、Zen of Python の作者である Tim Peters の意図どおりには最後の格言をまだ追加していません。Guido は、欠けている 20 番目の格言は「Tim Peters の奇妙な内輪ネタだ」と述べたと言われています。
20 番目の格言が欠けているにもかかわらず、Zen of Python は Python プログラマーの開発プロセスに大きな影響を与えたことから、2004 年に PEP 20 として標準化されました。Zen of Python に従うことは必須ではありませんが、知っておき、心に留めておくことが勧められます。もし教訓を忘れてしまっても、Python インタプリタから import this を実行すれば、すぐに思い出せます。
>>> 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!
本記事では、各教訓をもう少し詳しく取り上げていきます。
#1 美しい方が醜いよりよい
プログラマーである以上、高いスキルを持っていることは間違いありません。コードで問題を解決するのは容易ではありませんが、Pythonista としてはそれ以上が求められます。美しいコードは醜いコードより優れています。Python が急速に名声を高めた一因は、そのとっつきやすさにあります。シンプルで、読みやすく、エlegant なのです。Python 開発者として、pythonic な基準を保つ、美しいコードを書く責任があります。
美しいコードとは何でしょうか。これは主観的なものです—美は見る者の目に宿る、というわけです。自制のための単純な指針は、他の開発者にも容易に理解できる、クリーンで読みやすいコードを書くことです。例えば、次のとおりです。
"""
Initial code was created by Vishal Sharma
See: https://towardsdatascience.com/the-zen-of-python-a-guide-to-pythons-design-principles-93f3f76d088a
"""
def collatz(num):
if num % 2 == 0:
return num // 2
else:
return 3 * num + 1
number = input("Enter a number: ")
while number != 1:
number = collatz(int(number))
print(number)
"""
Enter a number: 5
16
8
4
2
1
"""
上のコードは確かに正しく動作しますが、必ずしも美しいとは言えません。collatz 関数には不要な else があり、次のように削除できます。
def collatz(num):
if num % 2 == 0:
return num // 2
return 3 * num + 1
このように、同じように動作するコードが 2 つあるなら、よりシンプルで読みやすく、理解しやすい方を選ぶべきです。
#2 明示は暗黙に勝る
どんな格言でも「自明」と片付けてしまうのは大きな間違いですが、そう言ってしまう人はいます。「明示は暗黙に勝る」の要点は、回りくどいコードよりも、はっきり書かれたコードが望ましいということです。コードの機能が難解な言い回しの背後に隠れないよう、できる限り努めてください。あなたのプログラムについて事前知識がない人でも、何が起きているか理解できるべきです。
#3 単純は複雑に勝る
夕食後の皿洗いに高圧洗浄機を持ち出した友人がいたら、きっと笑ってしまうでしょう。非合理的な解決策であるだけでなく、皿が傷む可能性も高いからです。同じように、単純なプログラミング課題に極めて複雑な解決策を適用するのは非常に非実用的です。たとえ賢く見えると思っても、解法を過度に複雑にする必要はありません。害の方が大きくなるかもしれませんし、Python では歓迎されません。
次の、文字列を反転させる関数を見てください。
def reverse_string(string):
if len(string) == 1:
return string
else:
return reverse_string(string[1:]) + string[0]
string = "kurtis"
print(reverse_string(string))
"""
sitruk
"""
このコードは確かに問題を解きますが、不要に複雑です。Python をよく理解していれば、インデックス指定で文字列を反転できると知っているはずです。
string = "kurtis"
# annonymous function solution
reverse_string = lambda x: x[::-1]
# function solution
def reverse(string):
return string[::-1]
print(reverse_string(string))
print(reverse(string))
"""
sitruk
sitruk
"""
#4 複雑は込み入っているよりよい
皿洗いに高圧洗浄機は現実的ではありません。流水のような単純な解法の方がよいでしょう。しかし、4x4 のレンジローバーを洗う課題ならどうでしょうか。前の格言とこの格言は、1 つの問題を解くのに単純な手法も複雑な手法もあり得ることを思い出させてくれます。4x4 のレンジローバーを洗うのは単純な問題ではなく、皿洗いと同じ手法を適用しようとすると、使えなくはないかもしれませんが、高圧洗浄機を使うよりもかえって込み入っています。したがって、基本的には単純さを優先すべきですが、単純さが非実用的な場面では、複雑さを選ぶ方がよい—単純さの限界を知ることが重要です。
#5 平坦は入れ子よりよい
プログラマーは機能を分けるために、カテゴリ、サブカテゴリ、サブサブカテゴリと整理したがります。一見整然としているようでも、そのような整理は、整理以上に混乱を招くことがあります。
すべてのコードをトップレイヤーの 1 つのモジュールに入れること自体は問題ではありません。つまり、特定の機能にアクセスするのに from spam.foo.bar.john.doe import chicken のようなことをする必要はないということです。サブカテゴリを増やすほど、コードは複雑になります。可能な限りフラットな構造を維持するよう努めてください。
#6 まばらは密よりよい
プログラマーは非常に知的な人々だと見なされています。だからといって、過剰に技巧的なハックで知性を誇示する必要はありません。よくあるのは「[複雑な作業]を 1 行のコードでやる方法」です。ワンライナーが正当化される場面もありますが、機能を 1 行に詰め込むために可読性を犠牲にする状況は避けてください。
次のコードを例に見てみましょう。
"""
Code source Al Sweigart
See: https://inventwithpython.com/blog/author/al-sweigart.html0
"""
print('\n'.join("%i bytes = %i bits which has %i possible values." % (j, j*8, 256**j-1) for j in (1 << i for i in range(8))))
"""
1 bytes = 8 bits which has 255 possible values.
2 bytes = 16 bits which has 65535 possible values.
4 bytes = 32 bits which has 4294967295 possible values.
8 bytes = 64 bits which has 18446744073709551615 possible values.
16 bytes = 128 bits which has 340282366920938463463374607431768211455 possible values.
32 bytes = 256 bits which has 115792089237316195423570985008687907853269984665640564039457584007913129639935 possible values.
64 bytes = 512 bits which has 13407807929942597099574024998205846127479365820592393377723561443721764030073546976801874298166903427690031858186486050853753882811946569946433649006084095 possible values.
128 bytes = 1024 bits which has 179769313486231590772930519078902473361797697894230657273430081157732675805500963132708477322407536021120113879871393357658789768814416622492847430639474124377767893424865485276302219601246094119453082952085005768838150682342462881473913110540827237163350510684586298239947245938479716304835356329624224137215 possible values.
"""
このコードは確かに動きますが、理解するのが悪夢のようです。詰め込みすぎで読みにくいのです。
同じ処理を、スパースなコードとして書くとどうなるか見てみましょう。
# Sparse example
bytes_and_bits = {j: j*8 for j in (1 << i for i in range(8))}
for bytes, bits in bytes_and_bits.items():
print(f"{bytes} bytes = {bits} which has {256**bytes-1} possible values")
"""
1 bytes = 8 which has 255 possible values
2 bytes = 16 which has 65535 possible values
4 bytes = 32 which has 4294967295 possible values
8 bytes = 64 which has 18446744073709551615 possible values
16 bytes = 128 which has 340282366920938463463374607431768211455 possible values
32 bytes = 256 which has 115792089237316195423570985008687907853269984665640564039457584007913129639935 possible values
64 bytes = 512 which has 13407807929942597099574024998205846127479365820592393377723561443721764030073546976801874298166903427690031858186486050853753882811946569946433649006084095 possible values
128 bytes = 1024 which has 179769313486231590772930519078902473361797697894230657273430081157732675805500963132708477322407536021120113879871393357658789768814416622492847430639474124377767893424865485276302219601246094119453082952085005768838150682342462881473913110540827237163350510684586298239947245938479716304835356329624224137215 possible values
"""
こちらの方がはるかに読みやすく、ワンライナーとまったく同じことを行っています。
#7 可読性は重要
もうお気づきのとおり、Python では可読性が物を言います。コードを書くのは 1 回ですが、読む機会は何度も訪れる可能性が高いのです。これを踏まえると、変数名や関数名から母音を省くのは得策ではありません。関数名を create_lst と create_list のどちらにするか迷ったら、後者を選びましょう。
#8 例外的なケースも、規則を破るほど特別ではない
Python[およびプログラミング一般]には守るべき数々のベストプラクティスがあります。我流でやるよりも、ベストプラクティスに従った方が、往々にして一貫性があり読みやすいコードになります。
#9 ただし、実用性は純粋性に勝る
教訓 9 は教訓 8 の延長です。規則に従うのがよいのは確かですが、無理に規則に合わせることで、かえって読みづらいコードになることもあります。したがって、各規則には例外があり得ます。もし自分の解法の方が、より実用的で、読みやすく、理解しやすいのであれば、確立されたベストプラクティスから逸れる方がよい場合もあります。
#10 エラーは決して黙って通過させるべきではない
サイレントエラーとは、例外を送出せずにエラーコードや None を返してしまうことです。エラーを黙殺して実行を続けるより、プログラムがクラッシュした方がよいのです。長期的には、エラーを黙らせることで、発見と修正がはるかに難しいバグを生む可能性があります。
#11 ただし、明示的に黙らせる場合を除く
教訓 11 は教訓 10 の延長です。プログラムが引き起こすエラーを無視したい場合もあります。そのような場合は、コードの中で明示的にエラーを黙らせるのがベストプラクティスです。
#12 曖昧さに直面したら、当て推量の誘惑に抗う
コンピュータは、指示されたことだけを実行します。コードが意図どおりに動かないのは、あなたがそう指示しているからです。闇雲にいくつも解決策を試し、どれかが当たるまで試行するのは悪手です—問題を解決するのではなく、覆い隠してしまうかもしれません。誘惑に抗いましょう。代わりに、問題のロジックを吟味し、適切な解決策を導くためにクリティカルシンキングを働かせてください。
#13 物事を行う方法は 1 つ—できれば 1 つだけ—明白であるべき
Perl のモットーは「やり方は 1 つとは限らない!」です。選択肢が多すぎると、かえって選べなくなることがあります。同じ目的を達成するコードの書き方がいくつもあるときにも同様のことが起こります。書き方の自由度は増しますが、読解の際には、そのコードが取り得たあらゆる書き方を学ばねばならなくなります。その余分な学習コストは不要です。
#14 ただし、そのやり方は最初は明白でないかもしれない—あなたがオランダ人でない限り
この格言は Tim Peters のユーモアの表れです。Python 言語の創始者であり、Python の Benevolent Dictator For Life(BDFL)である Guido van Rossum はオランダ人です。Python の言語規則を理解し、覚えておくのは、創始者以外の誰にとっても難しい、ということを思い出させるための冗談です。
#15 今は「決してやらない」よりよい
この格言は、無限ループに陥ったコードやハングするコードは、そうでないコードより悪い、と教えてくれます。
#16 ただし、「今すぐ」よりは「やらない方がまし」なことも多い
教訓 16 の続きです。プログラムを途中で打ち切って誤った結果を得るより、実行の完了を待つ方がよいということです。
#17 実装が説明しにくいなら、それは悪い考え
自分だけが理解できれば十分、というわけではありません。「自分の言いたいことは分かっている」では通用しません。プログラミングはチーム活動です。実装を仲間に説明できないなら、解法を複雑にしすぎている可能性が高いのです。
#18 実装が説明しやすいなら、よい考えかもしれない
ただし、説明しやすいコードだからといって、必ずしも良いコードだとは限りません。単に説明しやすいというだけのことです。それでも悪いコードである可能性はありますが、説明が容易であるという事実は、正しい方向に進んでいるサインです。
#19 名前空間はものすごく素晴らしいアイデア—もっと活用しよう!
名前空間は、プログラム内のオブジェクトに割り当てられた名前を整理するために Python で用いられる抽象です。特定の名前空間とスコープが与えられると、Python は、記号名を呼び出した際にどのオブジェクトを指しているかを判断できます。この格言が言っているのは、Python が内部で記号名を整理する方法は本当に見事だ、ということです。
まとめ
本記事では、Python プログラマーがクリーンで読みやすいコードを書くことを促すために作られた Zen of Python を、私たちなりに解釈して紹介しました。Zen of Python を、シームレスなコーディングの究極の青写真とみなす人もいれば、そこまで厳格には捉えない人もいます。まずは自分のコードにこれらの原則を適用し、どのように仕事の質が向上するかを実感してみてください。最適化されたコードの書き方をさらに学びたい場合は、DataCamp の Writing Efficient Python Code をご覧ください。