注目キーワード

[Python] Pythonコーディングルール集

目次

概要

Pythonコーディングルールをまとめる。
ついついルールがブレてしまうことがあるので、戒めを込めてまとめる。
まとまる順番は、ソースのページ通りではない。

また、追加情報、補足情報を別文献、情報源から取り入れて補完する。

主たるソースはこちら:

styleguide

Style guides for Google-originated open-source projects…

命名規則(Naming)

ソースにおける記載箇所はこちら

命名例
module_name, package_name, ClassName, method_name, ExceptionName, function_name, GLOBAL_CONSTANT_NAME, global_var_name, instance_var_name, function_parameter_name, local_var_name

関数名、変数名、ファイル名は説明的でなければならず、省略すべからず。
特に、プロジェクト外のコード読者にとって曖昧で馴染みのない略語を使用したり、単語内の文字を削除して略することがないようにすべし。

ファイル名の拡張子は、常に .py を使用すべし。
ダッシュは使用すべからず。

避けるべき名前

  • 1文字の名前は避けるべきである。ただし、以下の特別なケースでは許容される。
    • カウンターやイテレータ(例: i, j, k, v)
    • try/except 文において e を例外識別子として使うケース
    • with 文において、f をファイルハンドルとして使うケース

1文字の命名を乱用すべからず。
一般的に、変数のスコープに合わせて、記述する(変数名などの)名前の長さを決めるべし。
例えば、iは5行のコードブロックでは良い名前かもしれないが、複数のネストしたスコープの中では曖昧で識別しにくい名前となるだろう。

  • いかなるパッケージ名、モジュール名においても -(ダッシュ) は使うべきではない。
  • 先頭と末尾にアンダースコア(_)が2文字伴う名前は使うべきではない。Pythonの予約名と衝突する。
    • 書式 : __********__
      • __init__, __add__, __sub__, __repr__, __cmp__, …
  • 攻撃的であったり、公序良俗に反する言葉は、命名として使うべきではない。
  • 変数の型を名前の一部に含めることは不必要である。
    • 例) id_to_name_dict

命名規則(Naming Conventions)

Protected/private メソッド・プロパティ

  • "Internal "とは、モジュール内部、またはクラス内のprotectedまたはprivateを意味する。
  • 先頭にアンダースコア(_)を付けることで、モジュール変数や関数の保護のサポートを受けることができる。
    • リンターは保護されたメンバーのアクセスにフラグを立てる。

ダンダー(__)を使ったprivate化の注意点

  • インスタンス変数やメソッドの前にダブルアンダースコア(__、別名「dunder(ダンダー)」)を付けると、効果的にそのクラスのプライベート変数やメソッドになる(名前のマングリングを利用し)。
    • 読みやすさやテストのしやすさに影響する上、実際にはプライベートではないので、この使用はあまり推奨されない。
    • アンダースコアは1つが望ましい。

モジュール化:クラスや関数のまとめ方

  • 関連するクラスやトップレベルの関数をまとめてモジュールにする。
    • Javaのように、1つのモジュールに1つのクラスしか入れないという制限は無い。
  • クラス名には CapWords(=Camel Case) を使い、モジュール名には lower_with_under.py(小文字とアンダーバー) を使う。
    • CapWords.py という名前の古いモジュールもありますが、モジュールがたまたまクラス名にちなんで名付けられた場合に混乱を招くため、現在では推奨されていない。
      • 例) import StringIO と書いたのか、それとも from StringIO import StringIO と書いたのか?と混乱を招く。
    • パッケージ内のプライベートなモジュールについては、先頭にアンダーバーを1つ入れる。
      • 例) _some_private_module.py
        • C/C++ でコンパイルされたモジュールをプライベートモジュールとしている例が多い。

プロパティ

  • プロパティ(=クラスが保持する属性値)は、lowercase または lower_case_with_underscore に基づいて名前を付ける。
    • プライベート・プロパティにしたい場合は、先頭にアンダースコア1文字を付ける。
      • 例) _contents, _history

引数

  • 引数の名前は、ローカル変数と同様に lowercase または lower_case_with_underscore に基づいて付ける。
    • 例) text, separator

テストメソッド

  • アンダースコアは、test で始まる unittest メソッド名の中で、その名前が持つ意味の論理的な構成要素を分離するのに用いられるが、それらの構成要素が CapWords を使用している場合でも同様である。
    • 1つの可能なパターンは test<MethodUnderTest>_<state> で、例えば testPop_EmptyStack は問題が無い。テストメソッドの名前に「唯一の正解」は無い。

補足:名前のマングリングとは

 メソッドの先頭にダブルアンダースコア(=ダンダー)を付けると、Pythonインタプリタは、サブクラス(=基底クラスを継承するクラス)のメソッドと名前が衝突しないように密かに名前を変更する

  • 名前のマングリングの目的は、クラス継承時に 予期せぬ名前の衝突 を防ぐことにある。
  • 全てのメソッドやプロパティに、名前のマングリングを適用すると可読性の低下を招くので推奨されない。

数学的表記法について(Mathematical Notation)

数学的なロジックで、長いコードを記述する場合、参照する論文やアルゴリズム中で記述された変数と名前を一致させたい場合は、Pythonのコーディング・スタイルと反するような短い変数名が使われる。
その際には、すべての命名規則の出典をコメントやdocstringで参照するか、出典にアクセスできない場合は、命名規則を明確に文書化しておくこと。
PEP8に準拠した descriptive_names は、文脈を無視して遭遇する可能性が高い、公開されたAPIに優先的に使用される。

名前付けガイド

ブール型変数

  • ブール型変数(=論理値型変数)の名前には、hasis を付けると可読性が上がりやすい。
    • 例) is_connected, has_cache

コレクション型変数

  • コレクション型の変数(リスト、タプル)は、複数形の英単語 にすると良い。
    • 例) connected_users, cached_tables

辞書型変数

  • 辞書型変数には、明示的な名前を付ける(変数名から意味・役割が明確に理解できる)
    • 書式例 (キーの単語)_(値の単語)
      • 例) 人名をキー、住所を値として格納する辞書 persons_addresses

型情報を変数名に含めるべからず

  • 変数の名前に型情報を表す単語(list, dict, setなど)を含める必要はない。
    • Pythonは、関数アノテーション変数アノテーション をサポートしているため。

一般的すぎる単語は避けるべし

  • 一般的すぎる単語を使うべきではない。意味の範囲が広すぎて、理解の妨げになりかねない。
    • ドメインを限定的に絞り込んだ名前が好ましい
      • 例) 平方根を計算するメソッドの場合 compute よりも square の方が好ましい。

中身と一致しないパッケージ名・モジュール名は避けるべし

  • 中身に含まれている処理を推測できないパッケージ名、モジュール名は避けるべし。
    • 例) misc, tools, utils, common, core
    • 非常に小さなモジュールに細分化されたとしても、名前と中身が一致するように複数のもモジュールに分割するのが望ましい。

既存の名前との衝突を避けるべし

  • 既存の名前との衝突を避けるべきである。
    • 意味を理解するにあたり、既存の名前を使用せざるを得ない場合、接尾辞にアンダースコアを付ける か、別名、先頭1文字を変えるなどの対処をすると良い。
      • 例) 既存の名前の接尾辞にアンダースコアを付ける場合 or_
      • 例) class の代わりに用いる名前 klass, cls (この2つはよく使われている)

クラス名

簡潔かつ的確に、意味や目的が一目で理解できる名前を付けることが望ましい。
一般的なプラクティスとしては、接尾辞にクラスの型や特性を表す単語を使用するというものがある。

  • 例) SQLEngine, MimeTypes, StringWidget, TestCase

基底クラスの場合、接頭辞に BaseAbstruct を使用することが多い。

  • 例) BaseCookie, AbstructFormatter

Main関数

Pythonでは、ユニットテストだけでなく、pydocでもモジュールがインポート可能である必要がある。
モジュールファイルが実行ファイルとして使われることを意図している場合、その主な機能はmain()関数の中にあるべきである。
コードはメインプログラムを実行する前に __name__ == '__main__' であるかどうかを常にチェックし、モジュールが(実行ファイルではなく)インポートされたときには(メインプログラムが)実行されないように対処する必要がある。

コード例)

def main():
    ...

if __name__ == '__main__':
    main()

モジュールがインポートされると、トップレベルのコードがすべて実行されるので、関数の呼び出しやオブジェクトの作成など、pydoc 処理中に実行してはいけない処理を行わないように注意すべきである。

関数の長さ(行数)

小さくて焦点の合った機能であることが望ましい。
長い関数が適切な場合もあるため、関数の長さに厳しい制限は設けてはいない。

長い関数

  • 関数が 約40行 を超える場合は、プログラムの構造を崩さずに分割できるかどうかを検討すべきである。
    • 関数を短くシンプルにしておくと、他の人がコードを読んだり修正したりするのが容易になる。
    • 今、長い関数が完璧に動作していても、数ヶ月後に誰かがその関数を修正すると、新しい動作が追加されるかもしれず、見つけにくいバグが発生する可能性がある。
  • 長い関数の分割を検討すべき場合
    1. 長く複雑で、扱うのが困難な関数の場合
    2. エラー発生時のデバッグが困難な関数の場合
    3. 長い関数の一部を別の場所で活用したい場合

型アノテーション/型ヒント

一般規則

(google style より)

  • PEP-484 に精通すべし

    • PEP-484 は、なかなかのボリュームがあるので、別エントリで扱いたい感。
  • メソッドにおいては、適切な型情報を得るために必要な場合のみ、selfやclsを注釈(アノテート)する。

    @classmethod def create(cls: Type[T]) -> T: return cls()
  • 他の変数や戻り値の型を表現してはいけない場合は、 Any を使う。

  • モジュール内のすべての関数にアノテーションを付ける必要は無い。

    • 少なくとも、公開しているAPI群に対しては、アノテーションを付けるようにすべし。
  • 安全性や分かりやすさと、柔軟性の良いバランスを判断すべし。

  • 型に関連したエラーが発生しやすいコードにはアノテーションを付けるべし

  • 理解しづらいコードにはアノテーションを付けるべし。

  • 型の観点から安定してきたコードに注釈をつけるべし。

    • (経験則)多くの場合、成熟したコードのすべての関数にアノテーションを付けても、柔軟性はあまり損なわれない。

行の折り返し(Line Breaking)

まずは、既存の行の折り返し規則 を理解すべし。
型アノテーションをすると、1行1パラメータを記述することになるだろう。

【基本書式】

def my_method(self,
              first_var: int,
              second_var: Foo,
              third_var: Optional[Bar]) -> int:
  ...

1行に収まる場合は、以下のように記述しても良い。

def my_method(self, first_var: int) -> int:
  ...

関数名、最後のパラメータ、戻り値型の組み合わせが長すぎる場合、4文字インデントして改行すべし。
※【基本書式】に従って記述すると、最後の1行が長くなり、PEPの規定文字数 79文字 を超える場合

def my_method(
    self, first_var: int) -> Tuple[MyLongType1, MyLongType2, MyLongType3]:
  ...

戻り値型が最後のパラメータと同じ行に収まらない場合(=PEP規定の79文字制限を超える場合)、パラメータを4文字インデントして改行し、閉じ括弧をdefに合わせるのが望ましい。

def my_method(
    self, other_arg: Optional[MyLongType]
) -> Dict[OtherLongType, MyLongType]:
  ...

pylint は、閉じ括弧の位置を、新しい行において、開始括弧と同じ場所にすることを許可するが、これは可読性が落ちる。

# 可読性が落ちる
def my_method(self,
              other_arg: Optional[MyLongType]
             ) -> Dict[OtherLongType, MyLongType]:
  ...

上記の例のように、型記述を途中で改行しないようにすべし。
ただし、長すぎて1行に収まらない場合もある(サブタイプは改行しないようにすべし)。

def my_method(
    self,
    first_var: Tuple[List[MyLongType1],
                     List[MyLongType2]],
    second_var: List[Dict[
        MyLongType3, MyLongType4]]) -> None:
  ...

一つのパラメータにおいて、名前と型アノテーションが長すぎる場合は(=79文字制限を超える)、型のエイリアスの使用を検討されたし。
最後の手段は、コロンの後で改行し、4文字インデントする方法である。

# Good 
def my_function(
    long_variable_name:
        long_module_name.LongTypeName,  # 4文字インデントして改行している
) -> None:
  ...
# bad
No:
def my_function(
    long_variable_name: long_module_name.   # 型アノテーションの記述の途中で改行している
        LongTypeName,
) -> None:
  ...

デフォルト値

PEP-008に従って、型アノテーションとデフォルト値の両方を持つ引数にのみ、 = の周りにスペースを空ける。

# Good
def func(a: int = 0) -> int:
  ...
# bad
def func(a:int=0) -> int:
  ...

NoneType の扱い

Pythonの型システムでは、 NoneType第一義クラスの型であり、型アノテーションの目的においては、 NoneNoneType の別名である。
引数が None になる可能性がある場合は、それを宣言しなければならない。
Unionを使うことも可能だが、型が1種類だけの場合は Optional を使う。

暗黙的な Optional の代わりに 明示的な Optional を使用すること。
PEP-484 の初期版では、 a: str = Nonea: Optional[str] = None と解釈することが許容されていた。
しかし現在では、これはもはや好ましい動作ではなくなっている点に注意されたい。

# good
from typing import Union, Optional

# 引数a, b ともに割り当てられる型の種類は1種類なので、Optional を使用
def func(a: Optional[str], b: Optional[str] = None) -> str:
  ...
def multiple_nullable_union(a: Union[None, str, int]) -> str:
  ...
# bad
from typing import Union

# 明示的な Optional を使用すべきである
def nullable_union(a: Union[None, str]) -> str:
  ...

# これは、もはや古いアノテーション定義の仕方である
def implicit_optional(a: str = None) -> str:
  ...

型エイリアス

複合型のエイリアスを宣言することができる。

  • エイリアスの名前は、 CapWorded であること。
  • そのエイリアスが、エイリアス宣言をしたモジュール内でのみ使用される場合、アンダースコアを接頭辞に付けて _Private とすること。

型エイリアスの使用例には、モジュールの名前と型の名前を一緒にすると長すぎる場合などがある。

# モジュール内のみで使用される場合(privateな使い方)
_ShortName = module_with_long_name.TypeWithLongName

ComplexMap = Mapping[str, List[Tuple[int, int]]]

型チェックを無視する場合

特別なコメント # type: ignore を記述すると、その行の型チェックを無効にすることができる。
pytype には(lintと類似している)特定エラーを無効にするオプションが用意されている。

# pytype: disable=attribute-error

変数に型付けをする(Typing variables)

(引数ではない)変数の型を定義するには、以下の方法がある。

コメントで型を定義する方法

行末のコメントで type: (型) と記述すると、変数の型を定義できる。

a = SomeUndecoratedFunction()  # type: Foo

コロン: を使って型を定義する方法

次のように、コロン(:)で区切って型の定義を記述することで、変数の型を定義できる。

a: Foo = SomeUndecoratedFunction()

型付けしたリスト vs タプル

  • 型付けされたリストは、単一の型の要素だけを保持できる。
  • 型付けされたタプル、単一の型の要素だけを保持することも出来るし、異なる種類の型の要素の組みを保持することもできる。
    • 一般的には、型付けされたタプルは変数の戻り値に使用される傾向にある。
# リストの場合、単一の型だけ保持できる。
a = [1, 2, 3]  # type: List[int]

# タプルの場合、単一の型も、複数の型も保持できる。
b = (1, 2, 3)  # type: Tuple[int, ...]
c = (1, "2", 3.5)  # type: Tuple[int, str, float]

型変数(TypeVars)

Pythonの型変数は、ジェネリクスを利用する一般的な方法である。
typing モジュールの TypeVar を使うことで、型変数を作成できる。
以下の例では、型変数 T はすべての変数型を許容する Any型 である。

from typing import List, TypeVar
T = TypeVar("T")
...
def next(l: List[T]) -> T:
  return l.pop()

以下のように、受け付ける変数型の種類に制約を加えることができる。

# 受け付ける変数型は int, float, str の3種類
AddableType = TypeVar("AddableType", int, float, str)
def add(a: AddableType, b: AddableType) -> AddableType:
  return a + b

定義済みの型変数を使うこともできる。例えば AnyStrがある(bytesunicodeを受け付ける)

from typing import AnyStr
def check_length(x: AnyStr) -> AnyStr:
  if len(x) <= 42:
    return x
  raise ValueError()

ジェネリクス

Javaの言語機能で知られているジェネリクス・プログラミングがPythonでも扱うことができる。
変数の型そのものをパラメータとして扱うことができる。(参照1)
その特徴は、型を抽象化してコードの再利用性を向上させつつ、静的型付け言語の持つ型安全性を維持できることである。(Wikipedia)

簡便な例から入ると、コンテナ型が保持する要素の型を次のように定義することができる。

from typing import List

# 文字列を格納するlistであることを明示
a:List[str] = []
a.append("hello world!")

# 宣言された型と異なる整数型を加えようとするとエラーになる
# > Argument 1 to "append" of "list" has incompatible type "int"; expected "str"
a.append(1)
  • typing.List を使ったジェネリクスは Python3.9 以降では非推奨とされている。

String型を扱う際の注意

文字列型をアノテーションする際の適切な型は、Pythonのバージョンによって異なる点に注意が必要である。

  • Str を使うことが推奨されるが、 Text でも差し支えない。一貫性をもっていずれかを使用すべし。
  • バイナリデータを扱うコードでは bytes を使用すべし。
  • テキストデータ(Python2では str または unicode、Python3ではstr)を処理する Python2互換 のコードでは、Text を使用すべし。

関数の型アノテーションの例:

def deals_with_text_data_in_py3(x: str) -> str:
  ...
def deals_with_binary_data(x: bytes) -> bytes:
  ...
def py2_compatible_text_data_processor(x: Text) -> Text:
  ...

Python2互換のケースの中では、Text の代わりに str を使用する場合がある。

  • unicode は、Python2 には存在しないので使わないこと。
# NG
# Python2 で unicode は使えない。
def py2_code(x: str) -> unicode:
  ...

bytesstr の両方を受け取る引数がある場合、Union を使い、Pythonのバージョンを考慮した適切なペアを作るべし。

from typing import Text, Union
...

# Python3 だけなら str で良い
def py3_only(x: Union[bytes, str]) -> Union[bytes, str]:
  ...

# Python2 と互換性を保ちたい場合、 Text を使う
def py2_compatible(x: Union[bytes, Text]) -> Union[bytes, Text]:
  ...

import文の書式

import処理は、原則として1行ごとに分かれているべきである(=1行の中で複数のimportを行わない)

  • ただし from を使い、モジュールから複数要素を import することは許容されている。
    • 例) typing モジュールから変数型を読み込む場合
# good
import os
import sys
from subprocess import Popen, PIPE # from 句でモジュールを指定し、複数の要素をimportするのはOK
from typing import Mapping, Sequence # typing モジュールからの import は複数記述が許容される

# bad
# 1行で複数の import をしている
import os, sys

import文を記述する場所

import文を記述する場所は、pythonモジュールを実装するファイルの先頭部分で、以下の条件を満たす必要がある。

  1. モジュールの説明をするコメント・docstring の後
  2. モジュール内グローバル変数・定数より前

【import文を配置するコードレイアウトの例】

# モジュールの説明のコメント
# なんとかカントカ

import os
import sys

from some_3rd_party_module import picked_up_function

global_log_buffer = []
GLOBAL_CONST_SOME_THRETHOLD = 1.23456

import文の並び順

import文は、最も一般的なもの(=標準モジュールなど)から、最も一般的ではないもの(=自分で作ったプライベートなモジュールなど)にかけて、グループを分けて記述すべし。

  1. future import 文は、真っ先に記述すべし。
    from __future__ import absolute_import
    from __future__ import division
    from __future__ import print_function
  2. 続いて標準モジュールを読み込むimport文を記述する。
    import os
    import sys
  3. サードパーティー製モジュールやパッケージを読み込むimport文を記述する。
    import tensorflow as tf
  4. 同じリポジトリ内にある別プロジェクトのパッケージ・モジュール、およびアプリケーション固有のパッケージ・モジュールを読み込む import文を記述する。
    from otherproject.ai import mind
    from myproject.backend.some_package import some_function

    ※以前は、アプリケーション固有のパッケージ・モジュールを読み込むimport文を別グループにしていたが、今ではその記述スタイルは推奨されていない。

各import文のグループの中で、大文字・小文字を区別せずに 辞書的に(アルファベット順に)並べるべし。

上記を踏まえた import文 の記述例:

import collections
import queue
import sys

from absl import app
from absl import flags
import bs4
import cryptography
import tensorflow as tf

from book.genres import scifi
from myproject.backend import huxley
from myproject.backend.hgwells import time_machine
from myproject.backend.state_machine import main_loop
from otherproject.ai import body
from otherproject.ai import mind
from otherproject.ai import soul

import文で記述するパスの作法

PEP-8に基づいている。

import文に記述するパスは、絶対パス表記が望ましい。可読性が高く、読み手に必要な情報を提供できる。

  • 例)
    import mypkg.sibling
    from mypkg import sibling
    from mypkg.sibling import example

ただし、複雑なレイアウトのパッケージを扱う場合など、絶対パスが不必要に冗長になる場合、明示的に相対パスでimportすることが許容される。

  • 例)
    from . import sibling
    from .sibling import example

ワイルドカードによるimportは避けるべし

ワイルドカードによるインポート (from <module> import *) は避けるべし。
どういった要素が読み込まれたのか不明確であり、コードの読み手も、IDEなどコーディング支援ツールも混乱するだろう。

情報源

最新情報をチェックしよう!