背景
転職してそろそろ本格的にpythonを仕事で使いおうとしているので、復習も兼ねて前に調べたpythonic coding styleについてまとめてみようと思います。
UPDATE:
後編を更新しました。
このページの内容
PEP 8を順守するのはまずの基本です。
既存の日本語訳も含め、すでに既存記事が一杯ありますので、そちらを優先に見てほしいです。
原文
日本語訳
他Qiita記事:
Python のコーディング規約 PEP8 に準拠する
Pythonのコーディング規約PEP8を要約してみる(1)
作成目的
- 訳す+要約することで覚える
- Qiitaページ作成練習
- できるだけ多く人にPEP 8のことを知ってほしい
(何となくこういうのは定期的に誰かが掘り出すことによって布教される気がします。)
2回目はPEP 8にない内容についてまとめる予定です。
もし何か補足やご指摘(日本人じゃないので、日本語のご指摘も是非)、マサカリおねがいします。
それでは本題に〜
前提
- 可読性は大事:コードは書かれることより、読まれることが多い
- スタイルは変わっていくものである
- 一貫性は大事:同じ関数や同じモジュール内のスタイルを極力統一しよう
- プロジェクトのローカルコーディングスタイルと衝突したら、ローカルスタイルを優先
- 同プロジェクトの過去スタイルと衝突したら、既存スタイル優先
コードレイアウト
インデント
- 4スペース
- ソフトタブ
- 長いコードの改行法1ー同類コードのスタートを合わせる
test = long_function(var1, var2,
var3, var4)
- 長いコードの改行法2ー追加インデントでコードを区別する
def long_function(
var1, var2,var3,
var4):
print(val_one)
- 長いコードの改行法3ーHanging indent
test = long_function(
var1, var2, var3,
va4)
- 長いif文の改行時、コメントか追加のインデントを入れよう。
if (
は丁度1インデント分なので、コードが見づらくなる可能性がある
if (hoge1_is_true and
hoge2_is_true):
# コメント
do_something()
#OR
if (hoge1_is_true
and hoge2_is_true):
do something
- 改行されてる() []構造の閉じる方の括弧は、インデントあってもなくてもよい
my_list = [
1, 2, 3,
4, 5, 6
]
#OR
my_list = [
1, 2, 3,
4, 5, 6
]
一行の長さ
- コードはMAX 79文字にすること
- 形式制限の少ない文字列データやコメントはMAX 72文字
- 改行時はコードを括弧に入れるのは推奨
- with, assertなどすでに括弧が使われているものに対して""でもよい
括弧を使う:
if (hoge1 and hoge2
hoge3 and hoge4):
do_something
""を使う:
with open(filepath1, "r") as file1, \
open(filepath2, "r") as file2:
do_something
改行場所
可読性を上げるために、演算子の前に改行
revenue = gacha1_price * payers1
+ gacha2_price * payers2
+ gacha3_price * payers3
空行
- トップレベルクラスやグローバル関数間に2空行
- メソッド間は1空行
- 違う種類のメソッド群やコード群をよしなに空行を追加
- 同類one liner間は空行なくてもオッケー
ソースファイルEncoding
- 基本的にUTF-8を使うこと
- 例外:
- 特殊なテストケース
- どうしてもUTF-8に含まれない文字が入っている名前を書く必要がある時
インポート
- 直接
import
は全部別行で行うこと
import pandas
import numpy
-
from ... import
の場合複数でもよい
from sklearn.linear_model import LinearRegression, LogisticRegression
- importの場所は必ずモジュール説明の後、グローバル変数定義の前
- importは以下のグループに分け、グループ間に空行を挟む
- standard library
- 3rd party library
- local library
- 絶対インポートを使う。相対インポートを避ける
-
from <module> import *
を使わないこと!ネームスペースなしでやると、何がインポートされてるかわからないから
モジュールレベルのdunder声明場所
- モジュール説明以降
- import声明以前
-
__future__
importの後
"""This is the example module.
This module does stuff.
"""
from __future__ import barry_as_FLUFL
__all__ = ['a', 'b', 'c']
__version__ = '0.1'
__author__ = 'Cardinal Biggles'
import os
import sys
他
- 複合ステートメントを避けよう
#Yes
if foo == 'blah':
do_blah_thing()
do_one()
do_two()
do_three()
#No:
if foo == 'blah': do_blah_thing()
do_one(); do_two(); do_three()
クォーテーション
- pythonは
"
と'
を区別しないので、コード内統一していればオッケー - ただし三クォーテーションのコメントは
"""
を使おう
"""This is how you write
documentatoin string.
"""
コード内スペース
以下のスペースを避けよう
- 括弧系内の近辺
#Yes
buger(bread[2], {cheese: 1})
#No
buger( bread[ 2 ], { cheese: 1 } )
- コンマと閉じる括弧の間
#Yes
a_tuple = (0,)
#No
a_tuple = (0, )
- コンマ、セミコロン、コロンの直前
#Yes
if a: print x, y; x, y = y, x
#No
if a : print x , y ; x , y = y , x
- sliceの時コロンは演算子扱いなので、基本的に両側に同量のスペースを置く(省略の時以外)
#Yes
ham[1:9], ham[1:9:3], ham[:9:3], ham[1::3], ham[1:9:]
#No
ham[1: 9], ham[1 :9], ham[1:9 :3]
- slice時、コロンの両側は数式や関数コールの場合、スペースを入れよう
#Yes
ham[lower+offset : upper+offset], ham[: upper_fn(x) : step_fn(x)]
#No
ham[lower + offset:upper + offset]
- 関数コールの変数リスト直前
#Yes
func_call(params)
#No
func_call (params)
- リストや辞書のindex直前
#Yes
dct['key'] = lst[index]
#No
dct ['key'] = lst [index]
- 代入時、
=
の隣はスペース一個にしよう
#Yes
x = 1
y = 2
long_variable = 3
#No
x = 1
y = 2
long_variable = 3
- 行末の余計なスペース
- 関数コール時のkeyword変数への代入
#Yes
def complex(real, imag=0.0):
return magic(r=real, i=imag)
#No
def complex(real, imag = 0.0):
return magic(r = real, i = imag)
以下のスペースを入れよう
- 通常は下記演算子の両側にスペース一個ずつ
- 代入系
=, +=, -=
, etc - 比較系
== , < , > , != , <> , <= , >= , in , not in , is , is not
- boolean系
and , or , not
- 代入系
- 優先度の違う演算子が使用された時に、優先度低い方の演算子の両側に
- 一個以上のスペースを避けよう
- 両側のスペース数を同じにしよう
#Yes
i = i + 1
submitted += 1
x = x*2 - 1
hypot2 = x*x + y*y
c = (a+b) * (a-b)
#No
i=i+1
submitted +=1
x = x * 2 - 1
hypot2 = x * x + y * y
c = (a + b) * (a - b)
- 関数アノテーション時
- コロン後一個
-
->
の両側
#Yes
def munge(input: AnyStr): ...
def munge() -> AnyStr: ...
#No
def munge(input:AnyStr): ...
def munge()->PosInt: ...
- 変数アノテーションと通常代入が同時に発生している時の
=
にも両方入れる
#Yes
def munge(sep: AnyStr = None): ...
def munge(input: AnyStr, sep: AnyStr = None, limit=1000): ...
#No
def munge(input: AnyStr=None): ...
def munge(input: AnyStr, limit = 1000): ...
末尾コンマ
- つけてもよい
- 基本的に括弧で囲もう
#Yes
FILES = ('setup.cfg',)
#No
FILES = 'setup.cfg',
- 複数行表示
#Yes
FILES = [
'setup.cfg',
'tox.ini',
]
initialize(FILES,
error=True,
)
#No
FILES = ['setup.cfg', 'tox.ini',]
initialize(FILES, error=True,)
コメント
全般
- コードと整合性取れないコメントならない方がまし、常にコメントが最新であることを担保しよう
- コメントは極力ちゃんとした文書にしよう
- 短いコメントなら句点を省略してもよいが、基本的にちゃんとつけよう
- 文書の句点後にスペースを二つ入れよう
- コメントは極力英語で書こう。いつかコード作成者の言語をわからない人に読まれるかもしれないので
ブロックコメント
- 基本的に一部のコードに向けて書くもの
- 対象コードと同じインデントを取ろう
- 基本的に
#
でそれぞれの行を始める - 文書の段落間は
#
のみの行で分けること
インラインコメント
- 多用しない
- インラインコメントの前に2スペース以上空けよう
- インラインコメントは
#
で始める - 明らかすぎるインラインコメント意味ないので、避けよう
ドキュメンテーション説明
- 書き方はPEP257を参照
- モジュール、クラスやメソッドん説明に使われるので、
def
行の下に置こう - 複数行にわたる場合、終わりの
"""
は一行を独占させるけど、one linerの場合は同じ行で配置する
命名について
基本
- 今まで割とぐちゃぐちゃなので、これは絶対完璧にならないけど、とりあえず今後新しいもの書くときに極力こちらのルールに揃えよう
- ユーザーに見えるパブリックAPIの命名は基本的に実装ではなく機能を反映しよう
- どの命名スタイルが使われているかを認識することは、どこに使っているかを認識すると同じ重要。基本的に以下のパターンが存在する
b # 単一小文字
B # 単一大文字
lowercase # 全部小文字
lower_case_with_underscores # _付き小文字
UPPERCASE # 全部大文字
UPPER_CASE_WITH_UNDERSCORES # _付き大文字
CapitalizedWords # 大文字イニシャル
CapitalizeAbbreviationLikeHTTP # 大文字イニシャルの場合、固有略称は全部大文字に
mixedCase # 複合
Capitalized_Words_With_Underscores #醜い大文字イニシャル + _
_single_leading_underscore # 内部のみ使用する時、privateとか
single_trailing_underscore_ # Pythonキーワードとのコンフリクトを避けるため
__double_leading_underscore # クラス変数を命名するときにの修飾
__double_leading_and_trailing_underscore__ # 絶対自分で定義しない
命名スタイルの利用
-
joined_lower
はモジュール名、関数、メソッド、インスタンス変数、グローバル変数に使う -
JOINED_UPPER
は定数に使う -
CapWords
はクラス、タイプ変数に使う - 例外はクラスなので、同様CapWordsルールで、ただし最後に必ず
Error
を入れる - 'l'(lower L), 'O'(Oh), 'I'(Upper i)を絶対単独で使わない。見分けづらい
他命名ルール
- インスタンスメソッドの最初の変数はself, クラスメソッドの最初の変数はcls。変数とkeywordが衝突したら、最後に'_'を入れて対応する
- パブリックメソッドの先頭に'_'はない
- 簡単なパブリックデータに対して、アクセッサーを用意するより、直接パラメタを公開した方がよい
- 継承される予定のクラス内の変数は、変数名の先頭にダブルアンダースコアを入れることで、名前のコンフリクトを避けることが可能
- 後方互換は基本的にpublic interfaceにのみ適用させる。
- publicとprivateインターフェースを識別しやすくするために、モジュールを定義するときにpublic interfaceを
__all__
にリストの形で定義しよう