PEP 8って知ってます? Pythonで標準的なコーディング規約ですよね。でも、どこまでその内容を理解できていますか? PEP 8の理解度を一緒に試してみましょう。
Pythonでは公式なコーディングスタイルガイドとしてPEP 8が提供されている。以下のコード片の中で、PEP 8に従った書き方となっているものはどれか?(複数選択可)
# 1
mytuple = (0,)
# 2
l = [0, 1, 2]
# 3
myfunc = lambda x: x + 1
# 4
if val is not None:
do_some_work(val)
# 5
res = (long_name_1 +
long_long_name_2 +
long_name_3 -
long_long_name_4)
どうもHPかわさきです。
今回はPEP 8(Pythonコーディングスタイルガイド)に従った書き方をしているコード片を選ぶ問題です。好評なら、第2弾、第3弾も考えてみます。こんなクイズがあると面白いなというのがあれば、HPかわさきに教えてくださいね。
あと、画像の中にはトンチンカンなことを言っている人がいますが、あんまり気にしないであげてくださいね。
答えを以下の画像に示します。PEP 8に従った書き方になっているのを「OK!」、そうでないものを「NG!」としています。
それぞれについての説明は解説パートでしていくことにしましょう。
イングランドのプレミアリーグに所属するマンチェスター・シティFCのジョゼップ・グアルディオラ監督の愛称「Pep」と勘違いしていたと彼も分かってくれたようです。よかった。よかった。
以下では問題に挙げたコード片ごとに、PEP 8ではどのようなスタイルが推奨されているかを説明していきます(PEP 8から引用している部分は筆者による訳です)。
最初のコード片は以下のようなものでした。
mytuple = (0,)
単にタプルを変数に代入するだけの文ですが、これを以下のようにしていることはありませんか?
mytuple = (0, )
後者の書き方は実は推奨されていません。PEP 8の「Whitespace in Expressions and Statements」では次のようなときには空白文字を入れないことが推奨されています。
リンク先では空白文字を入れるべきではない状況が他にもいろいろと挙げられていますが、ここでは「末尾のカンマ『,』と閉じかっこの間」に注目しましょう。「末尾のカンマ」とは幾つか(1個以上)の要素をカンマで区切って並べるときの最後のカンマのことです。「(0, )」のカンマはまさにそうしたカンマですよね。そして、その直後にタプルを閉じる閉じかっこが続いています。このようなときには空白文字を入れないことが推奨されています。
問題文では「mytuple = (0,)」のように空白文字が入っていません。なので、これは「OK!」ということになります。
2つ目のコード片は次のようなものでした。
l = [0, 1, 2]
全く問題ないように思えますが、どこがダメなのでしょうか? PEP 8の「Prescriptive: Naming Conventions」には『小文字のエル「l」や大文字のオー「O」、大文字のアイ「I」を1文字の変数名として絶対に使わないこと』とあります。
予想は付くでしょうが、これらの文字が単独で登場した場合、見分けにくいからです。「1」(イチ)なのか「l」(エル)なのか、「0」(ゼロ)なのか「O」(オー)なのか、「l」(エル)なのか「I」(アイ)なのか。よく分からないですよね。
というわけで、リストを代入するからといって、変数名を安易に「l」としているこのコードは「NG!」です。
とはいえ、対話環境などで、ちょっとコードを試したいときなどに筆者はついやっちゃうことがあります(苦笑い)。でも、普段からそういう名前を使わないような習慣を付けておくべきだとは自覚しています。というわけで、これからは「mylist」や「foo」などを使うようにしたいと思います(皆さんも普段から一時的に使用する変数の名前を考えておくようにしましょう)。
3つ目のコード片は次のようなものでした。
myfunc = lambda x: x + 1
ラムダ式を変数に代入すれば、次のように呼び出しが可能です。
val = myfunc(10)
しかし、PEP 8の「Programming Recommendations」では「ラムダ式を識別子に直接束縛する代入文を使うのではなく、常にdef文を使う」とされています。
def文で関数を定義すると、関数オブジェクトの名前が明確になります。これはラムダ式を変数に代入したときとの違いの一つです。試してみましょう。
def myfunc(x):
return x + 1
print(myfunc)
myfunc = lambda x: x + 1
print(myfunc)
これを実行してみると、次のようになります。
def文で定義した関数では関数名の「myfunc」が得られていますが、ラムダ式を代入した変数myfuncについては関数名の代わりに「<lambda>」と表示されています。ささいな差ですが、デバッグ時にトレースバックを追いかけるときなど、関数名が明確な方が役立つときがあるはずです。関数として定義したいものはdef文を使用するようにしましょう。
ラムダ式の最大の利点は、大きな式の一部に関数的なものを(事前にそれを定義することなく)含められることです。
d = [-10, 2, 8, 12, -5]
result = sorted(d, key=lambda x: abs(x))
この例ではラムダ式をsorted関数呼び出しのkeyパラメーターに渡しています(実際には「key=abs」で済むのですが、ここでは例なのでご容赦を)。ラムダ式はこんな感じで使うものだということは覚えておきましょう。
そういうわけで、代入文でラムダ式を変数に束縛しているこの表記は「NG!」です。
4つ目のコード片は次のようなものでした。
if val is not None:
do_some_work(val)
これは変数valの値がNoneでないことを確認しています。実は、このコード片は次のようにも書けます。
if not val is None:
do_some_work(val)
こちらはis演算子で変数valの値がNoneかどうかを調べて、その結果を否定することで結果的には上のコードと同じことをしています。しかし、PEP 8の「Programming Recommendations」では「not ... isではなく、is not演算子を使うこと。これらの式は機能的には同等だが、is not演算子の方が読みやすく、推奨される」とあります。
確かに「val is not None」とまとめて書かれている方が「not val is None」の方がスッと頭に入ってくると思いませんか?
そのような書き方をしているので、これは「OK!」です。
最後のコード片は以下のようなものでした。
res = (long_name_1 +
long_long_name_2 +
long_name_3 -
long_long_name_4)
少し説明が必要ですが、これは右辺の式全体をかっこ「()」で囲むことで、非明示的な行継続ができるようにしています(かっこがないと、バックスラッシュで明示的に行を継続させる必要があります)。その上で、問題のコード片では演算子を各変数の後に置いています。
しかし、このコード片は次のように書くことがPEP 8の「Should a Line Break Before or After a Binary Operator?」で推奨されています。
res = (long_name_1
+ long_long_name_2
+ long_name_3
- long_long_name_4)
最初のコードでは、変数名の長さがそれぞれに違うので、その後にある演算子の位置もバラバラです。これでは、何の演算をするのかを見分けるために、目の動きが余計に必要になるでしょう。書き直したコードでは変数名の長さに関係なく、演算子の位置は常に同じで、何をしたいかが一目瞭然です。
そうした理由からPEP 8では後者の書き方が推奨されています。
というわけで、問題文で示した「二項演算子を変数の後において、そこで改行する」ような書き方は「NG!」となります。
PEP 8は、Pythonコードを書く人にとっては最も一般的なコーディングスタイルガイドで、多くのプロジェクトで採用されており、従うべきとされる場面も多いでしょう。でも、その冒頭には「A Foolish Consistency is the Hobgoblin of Little Minds」(愚かな一貫性は小さな心の中の魔物)ともあります。つまり「一貫性にこだわりすぎるのはよくないよ」ということです。
チームで開発しているのであれば、チームのコーディングスタイルに合わせればよいでしょうし、既存の資産を改修しているのであれば、既存のコードのスタイルに合わせる必要があります。そうしたところまで勘案しながら、一定のスタイルを保つようにしましょう。
PEP 8については「Python入門」の「Pythonコーディングスタイルガイド」でも、より広範に取り上げています。興味のある方はそちらも読んでくれるとうれしいです。
Copyright© Digital Advantage Corp. All Rights Reserved.