以下は、2020年11月時点の、Google Python Style Guide の全文を、kanda.motohiro@gmail.com が、 Google 翻訳にかけ、その結果を修正したものです。原文のライセンスは、CC-By 3.0 なのですが、機械翻訳結果の著作権と使用条件が不明なので、この文書全体の配布ライセンスも不明です。
スクラッチから翻訳しようとしたのだが、機械翻訳の結果があまりにも自然なので、使うことにした。
その他の言語のスタイルガイドの訳:
Google Java Style Guide(非公式和訳)
Google JavaScript Style Guide 和訳
Google Common Lisp スタイルガイド 日本語訳
他にもあったら教えてね。みなさまに感謝。
1 背景
Pythonは、Googleで使用される主要な動的言語です。このスタイルガイドは、Pythonプログラムの推奨事項と禁止事項のリストです。
コードを正しくフォーマットできるように、Vimの設定ファイルを作成しました。Emacsの場合、デフォルト設定で問題ありません。
多くのチームは、yapf オートフォーマッターを使用して、フォーマットについての議論を避けています。
2 Python言語ルール
2.1 Lint
このpylintrcを使用して、あなたのコードに pylint を実行しなさい。
2.1.1 定義
pylint は、Pythonソースコードのバグやスタイルの問題を見つけるためのツールです。CやC ++などの動的でない言語のコンパイラーによって通常キャッチされる問題を検出します。Pythonの動的な性質により、一部の警告は正しくない場合があります。ただし、誤った警告はかなり少ないはずです。
2.1.2長所
タイプミス、using-vars-before-assignmentなどの見逃しやすいエラーをキャッチします。
2.1.3短所
pylint は完璧ではありません。それを利用するために、私たちは時々:a)それを回避するコーディングをするか、 b)その警告を抑制するか、 c)それを改善する必要があるでしょう。
2.1.4決定
あなたのコードに、pylint を実行するように してください。
他の問題が隠されないように、警告が不適切な場合は警告を抑制します。警告を抑制するために、行レベルのコメントを設定できます。
dict = 'something awful' # Bad Idea... pylint: disable=redefined-builtin
pylint 警告はそれぞれ記号名(empty-docstring)で識別されます。Google固有の警告は g- で始まります。
記号名から抑制の理由が明確でない場合は、説明を追加してください。
この方法で抑制すると、抑制を簡単に検索して再検討できるという利点があります。
次のpylint 手順を実行すると、警告のリストを取得できます 。
pylint --list-msgs
特定のメッセージの詳細を取得するには、次を使用します。
pylint --help-msg=C6409
非推奨の古い形式pylint: disable-msgの代わりに、pylint: disable を使います。
関数の先頭で変数を削除することで、未使用の引数の警告を抑制することができます。削除する理由を説明するコメントを常に含めてください。「 Unused」で十分です。例えば:
def viking_cafe_order(spam, beans, eggs=None):
del beans, eggs # Unused by vikings.
return spam + spam + spam
この警告を抑制する他の一般的な形式には、未使用の引数の識別子として'_ 'を使用するか、引数名の前に ' unused_'を付けるか、それらを ' _'に代入することが含まれます。これらのやり方は許可されていますが、推奨されなくなりました。これらは、引数を名前で渡すコール元をこわし、その引数が実際に使われないことを強制しません。
2.2 Import
個々のクラスや関数ではなく、パッケージとモジュールにのみ import ステートメントを使用してください。タイピングモジュールからのインポートには明示的な免除があることに注意してください。
2.2.1定義
あるモジュールから別のモジュールにコードを共有するための再利用メカニズム。
2.2.2長所
名前空間の管理規則は単純です。各識別子のソースは一貫した方法で示されます。x.Objは、オブジェクトObjがモジュール x で定義されていることを示します。
2.2.3短所
モジュール名はそれでも衝突する可能性があります。一部のモジュール名は不便なほど長いです。
2.2.4決定
パッケージとモジュールのインポートに import x を使用します。
xがパッケージ接頭辞で、yが接頭辞なしのモジュール名の場合、from x import y を使います。
y と名前の付いた2つのモジュールをインポートする場合、またはy が不便なほど長い名前の場合に from x import y as z を使用します。
z が標準の略語である場合にのみimport y as z を使用します(例:np for numpy)。
たとえば、モジュールsound.effects.echoは次のようにインポートできます。
from sound.effects import echo
...
echo.EchoFilter(input, output, delay=0.7, atten=4)
インポートで相対名を使用しないでください。モジュールが同じパッケージに含まれている場合でも、完全なパッケージ名を使用してください。これにより、意図せずにパッケージを2回インポートするのを防ぐことができます。
タイピングモジュールおよび six.movesモジュールからのインポートは、 このルールから除外されます。
モジュールの場所の絶対パス名を使用して、各モジュールをインポートします。
2.3.1長所
モジュール名の競合や、モジュールの検索パスが作成者の期待どおりでないことから起きる、誤ったインポートを回避します。モジュールを見つけやすくします。
2.3.2短所
パッケージ階層を複製する必要があるため、コードのデプロイが難しくなります。最新のデプロイメントメカニズムでは実際には問題ではありません。
2.3.3決定
すべての新しいコードは、完全なパッケージ名で各モジュールをインポートする必要があります。
インポートは次のようになります。
はい:
# Reference absl.flags in code with the complete name (verbose).
import absl.flags
from doctor.who import jodie
FLAGS = absl.flags.FLAGS
# Reference flags in code with just the module name (common).
from absl import flags
from doctor.who import jodie
FLAGS = flags.FLAGS
いいえ:(このファイルは doctor/who/ にあり、そこには、jodie.pyもあると仮定します)
# Unclear what module the author wanted and what will be imported. The actual
# import behavior depends on external factors controlling sys.path.
# Which possible jodie module did the author intend to import?
import jodie
一部の環境でそうであっても、メインバイナリが配置されているディレクトリがsys.path内に存在すると想定しないでください 。この場合、コードは、import jodie は、ローカルjodie.pyではなく、jodieという名前のサードパーティまたはトップレベルのパッケージを参照していると想定する必要があります。
例外は許可されますが、慎重に使用する必要があります。
2.4.1定義
例外は、エラーやその他の例外的な状態を処理するために、コードブロックの通常の制御フローから抜け出す手段です。
2.4.2長所
通常操作のコードの制御フローは、エラー処理コードによって乱雑になりません。また、特定の条件が発生したときに、制御フローが複数のフレームをスキップできるようにします。たとえば、エラーコードを引き渡す代わりに、1つのステップでN個のネストされた関数から戻ることができます。
2.4.3短所
制御フローが混乱する可能性があります。ライブラリ呼び出しを行うときにエラーケースを見逃しがちです。
2.4.4決定
例外は特定の条件に従う必要があります。
それが意味を持つ場合は、組み込みの例外クラスを利用してください。たとえば、ValueErrorを上げて、違反した前提条件などのプログラミングミスを示します(負の数が渡されたが正の数が必要な場合など)。パブリックAPIの引数値を検証するためにassertステートメントを使用しないでください。assertは、正しい使用法を強制したり、予期しないイベントが発生したことを示すためではなく、内部の正確性を確保するために使用されます。後者の場合に例外が必要な場合は、raiseステートメントを使用してください。例えば:
はい:
def connect_to_next_port(self, minimum):
"""Connects to the next available port.
Args:
minimum: A port value greater or equal to 1024.
Returns:
The new minimum port.
Raises:
ConnectionError: If no available port is found.
"""
if minimum < 1024:
# Note that this raising of ValueError is not mentioned in the doc
# string's "Raises:" section because it is not appropriate to
# guarantee this specific behavioral reaction to API misuse.
raise ValueError(f'Min. port must be at least 1024, not {minimum}.')
port = self._find_next_open_port(minimum)
if not port:
raise ConnectionError(
f'Could not connect to service on port {minimum} or higher.')
assert port >= minimum, (
f'Unexpected port {port} when minimum was {minimum}.')
return port
いいえ:
def connect_to_next_port(self, minimum):
"""Connects to the next available port.
Args:
minimum: A port value greater or equal to 1024.
Returns:
The new minimum port.
"""
assert minimum >= 1024, 'Minimum port must be at least 1024.'
port = self._find_next_open_port(minimum)
assert port is not None
return port
ライブラリまたはパッケージは、独自の例外を定義する場合があります。その際、既存の例外クラスから継承する必要があります。例外名はErrorで終わる必要が あり、スタッター(foo.FooError)を導入するべきではありません。
次の場合を除いてcatch-all の except:、ステートメント、catch Exceptionまたは StandardErrorを使用しないでください。
再び例外を再発生させる、または
例外が伝播されず、代わりに記録および抑制される分離ポイントをプログラムに作成する。たとえば、スレッドの最も外側のブロックを保護することにより、スレッドがクラッシュするのを防ぎます。
Pythonはこの点で非常に寛容であり、except: は名前のつづりの間違い、sys.exit()呼び出し、Ctrl + C割り込み、単体テストの失敗、および単にキャッチしたくないその他のあらゆる種類の例外を含むすべてを実際にキャッチします。
try/exceptブロック内のコードの量を最小限に抑えます。tryの本体が大きいほど、例外が発生するとは思わなかったコード行によって例外が発生する可能性が高くなります。そのような場合、 try/exceptブロックは実際のエラーを隠します。
finally句を使用して、tryブロックで例外が発生したかどうかに関係なくコードを実行します。これは、クリーンアップ、つまりファイルを閉じるときに役立つことがよくあります。
グローバル変数は避けてください。
2.5.1定義
モジュールレベルまたはクラス属性として宣言される変数。
2.5.2長所
たまに便利です。
2.5.3短所
グローバル変数への割り当てはモジュールが最初にインポートされたときに行われるため、インポート中にモジュールの動作を変更する可能性があります。
2.5.4決定
グローバル変数は避けてください。
これらは技術的には変数ですが、モジュールレベルの定数が許可および推奨されています。例:MAX_HOLY_HANDGRENADE_COUNT = 3。定数には、アンダースコア付きのすべての大文字を使用して名前を付ける必要があります。以下の命名を参照してください。
必要ならば、グローバルをモジュールレベルで宣言し、名前の前に _ を付けてモジュールの内部に作成するのが良いでしょう。外部アクセスは、パブリックモジュールレベルの関数を介して実行する必要があります。以下の命名を参照してください。
ネストされたローカル関数またはクラスは、ローカル変数を閉じるために使用する場合は問題ありません。インナークラスは大丈夫です。
2.6.1定義
クラスは、メソッド、関数、またはクラスの内部で定義できます。関数は、メソッドまたは関数内で定義できます。ネストされた関数は、囲んでいるスコープで定義された変数に読み取り専用でアクセスできます。
2.6.2長所
非常に限られたスコープ内でのみ使用されるユーティリティクラスと関数の定義を許可します。非常に ADT -y。デコレータの実装に一般的に使用されます。
2.6.3短所
ネストされたクラスまたはローカルクラスのインスタンスはpickle化できません。入れ子関数とクラスを直接テストすることはできません。ネストすると、外部関数が長くなり、読みにくくなる可能性があります。
2.6.4決定
いくつかの注意点に気を付ければ、問題ありません。ローカル値を閉じる場合を除いて、ネストされた関数またはクラスは避けてください。モジュールのユーザーから関数を隠すためだけに関数をネストしないでください。代わりに、モジュールレベルで名前の前に_を付けて、テストで引き続きアクセスできるようにします。
単純なケースならば使用しても大丈夫です。
2.7.1定義
リスト、辞書、Set の内包表記と、ジェネレータ式は、伝統的なループ、map()、filter()またはlambdaの使用に頼ることなくコンテナタイプとイテレータを作成するための簡潔かつ効率的な方法を提供します。
2.7.2長所
単純な内包表記は、他のdict、list、またはsetの作成手法よりも明確で単純な場合があります。ジェネレータ式は、リスト全体の作成を回避するため、非常に効率的です。
2.7.3短所
複雑な内包表記やジェネレータ式は読みにくい場合があります。
2.7.4決定
単純なケースならば使用しても大丈夫です。マッピング式、for句、フィルター式の各部分は、1行に収まる必要があります。複数のfor句またはフィルター式は許可されていません。物事がより複雑になる場合は、代わりにループを使用してください。
Yes:
result = [mapping_expr for value in iterable if filter_expr]
result = [{'key': value} for value in iterable
if a_long_filter_expression(value)]
result = [complicated_transform(x)
for x in iterable if predicate(x)]
descriptive_name = [
transform({'key': key, 'value': value}, color='black')
for key, value in generate_iterable(some_input)
if complicated_condition_is_met(key, value)
]
result = []
for x in range(10):
for y in range(5):
if x * y > 10:
result.append((x, y))
return {x: complicated_transform(x)
for x in long_generator_function(parameter)
if x is not None}
squares_generator = (x**2 for x in range(10))
unique_names = {user.name for user in users if user is not None}
eat(jelly_bean for jelly_bean in jelly_beans
if jelly_bean.color == 'black')
No:
result = [complicated_transform(
x, some_argument=x+1)
for x in iterable if predicate(x)]
result = [(x, y) for x in range(10) for y in range(5) if x * y > 10]
return ((x, y, z)
for x in range(5)
for y in range(5)
if x != y
for z in range(5)
if y != z)
リスト、辞書、ファイルなど、デフォルトのイテレータと演算子をサポートする型にはそれらを使用します。
2.8.1定義
ディクショナリやリストなどのコンテナタイプは、デフォルトのイテレータとメンバーシップテスト演算子(「in」と「not in」)を定義します。
2.8.2長所
デフォルトのイテレータと演算子はシンプルで効率的です。これらは、余分なメソッド呼び出しなしで、操作を直接表現します。デフォルトの演算子を使用する関数は汎用です。操作をサポートするすべてのタイプで使用できます。
2.8.3短所
メソッド名(たとえばhas_key() は、辞書を意味する)を読んでも、オブジェクトのタイプを判別することはできません。これは利点でもあります。
2.8.4決定
リスト、辞書、ファイルなど、デフォルトのイテレータと演算子をサポートする型にはそれらを使用します。組み込み型はイテレータメソッドも定義します。リストを返すメソッドよりもこれらのメソッドを優先して下さい。ただし、コンテナーを反復処理するときにコンテナーを変更しないでください。必要な場合を除いて、dict.iter*() のような、Python2固有の反復法を使用しないでください。
Yes: for key in adict: ...
if key not in adict: ...
if obj in alist: ...
for line in afile: ...
for k, v in adict.items(): ...
for k, v in six.iteritems(adict): ...
No: for key in adict.keys(): ...
if not adict.has_key(key): ...
for line in afile.readlines(): ...
for k, v in dict.iteritems(): ...
必要に応じてジェネレーターを使用してください。
2.9定義
ジェネレーター関数は、yieldステートメントを実行するたびに値を生成するイテレーターを返します。値が生成された後、次の値が必要になるまで、ジェネレーター関数の実行時の状態が一時停止されます。
2.9.2長所
ローカル変数の状態と制御フローが呼び出しごとに保持されるため、コードが単純になります。ジェネレータは、値のリスト全体を一度に作成する関数よりも少ないメモリを使用します。
2.9.3短所
無し。
2.9.4決定
結構です。ジェネレーター関数のdocstringでは、「Returns:」ではなく「Yields:」を使用してください。
ワンライナーならば大丈夫。
2.10.1定義
ラムダは、ステートメントではなく、式の中で無名関数を定義します。これらはmap()や、filter()などの高階関数のコールバックまたは演算子を定義するためによく使用されます。
2.10.2長所
便利。
2.10.3短所
ローカル関数よりも読み取りとデバッグが困難です。名前がないということは、スタックトレースを理解するのがより難しいことを意味します。関数には式のみが含まれる可能性があるため、表現力は制限されます。
2.10.4決定
ワンライナーならば使用しても大丈夫です。ラムダ関数内のコードが60〜80文字より長い場合は、通常のネストされた関数として定義することをお 勧めします。
乗算などの一般的な演算では、ラムダ関数の代わりにoperatorモジュールの関数を使用します。たとえば、 lambda x, y: x * yより、operator.mulを優先します。
単純なケースならば大丈夫です。
2.11.1定義
条件式(「三項演算子」と呼ばれることもあります)は、ifステートメントの構文を短くするメカニズムです。例:x = 1 if cond else 2。
2.11.2長所
ifステートメントよりも短くて便利です。
2.11.3短所
ifステートメントよりも読みにくい場合があります。式が長いと、条件を見つけるのが難しい場合があります。
2.11.4決定
単純なケースならば使用しても大丈夫です。true-expression、if-expression、else-expressionの各部分は、1行に収まる必要があります。事態がさらに複雑になる場合は、完全なifステートメントを使用してください。
one_line = 'yes' if predicate(value) else 'no'
slightly_split = ('yes' if predicate(value)
else 'no, nein, nyet')
the_longest_ternary_style_that_can_be_done = (
'yes, true, affirmative, confirmed, correct'
if predicate(value)
else 'no, false, negative, nay')
bad_line_breaking = ('yes' if predicate(value) else
'no')
portion_too_long = ('yes'
if some_long_module.some_long_predicate_function(
really_long_variable_name)
else 'no, false, negative, nay')
ほとんどの場合大丈夫です。
2.12.1定義
関数のパラメータリストの最後にある変数の値を指定できます(例:def foo(a, b=0):。fooが1つの引数のみで呼び出された場合、bは0に設定されます。2つの引数で呼び出された場合、bは2番目の引数の値を持ちます。
2.12.2長所
多くの場合、多くのデフォルト値を使用する関数がありますが、まれにデフォルトをオーバーライドしたい場合があります。デフォルトの引数値は、まれな例外のために多くの関数を定義する必要なしに、これを行う簡単な方法を提供します。Pythonはオーバーロードされたメソッド/関数をサポートしていないため、デフォルトの引数はオーバーロード動作を「偽造」する簡単な方法です。
2.12.3短所
デフォルトの引数は、モジュールのロード時に1回評価されます。引数がリストや辞書などの可変オブジェクトの場合、これにより問題が発生する可能性があります。関数がオブジェクトを変更する場合(たとえば、リストに項目を追加することによって)、デフォルト値が変更されます。
2.12.4決定
次の注意事項を使用して使用できます。
関数またはメソッド定義のデフォルト値として可変オブジェクトを使用しないでください。
Yes: def foo(a, b=None):
if b is None:
b = []
Yes: def foo(a, b: Optional[Sequence] = None):
if b is None:
b = []
Yes: def foo(a, b: Sequence = ()): # Empty tuple OK since tuples are immutable
...
No: def foo(a, b=[]):
...
No: def foo(a, b=time.time()): # The time the module was loaded???
...
No: def foo(a, b=FLAGS.my_thing): # sys.argv has not yet been parsed...
...
No: def foo(a, b: Mapping = {}): # Could still get passed to unchecked code
...
以前ならばシンプルで軽量なアクセサーまたはセッターメソッドを使用するであろうデータにアクセスまたは設定するためにプロパティを使用します。
2.13.1定義
計算が軽量な場合に、属性を標準の属性アクセスとして取得および設定するメソッド呼び出しをラップする方法。
2.13.2長所
単純な属性アクセスのための明示的なgetおよびsetメソッド呼び出しを排除することにより、読みやすさが向上します。計算を遅延評価することができます。クラスのインターフェースを維持するためのPython的な方法と見なされています。パフォーマンスの観点から、プロパティを許可すると、直接変数アクセスが妥当な場合に、単純なアクセサーメソッドを必要とせずにバイパスできます。これはまた、インターフェイスを壊すことなく、アクセサメソッドを将来追加することもできます。
2.13.3短所
Python 2 では、objectから継承する必要があります。演算子のオーバーロードのように、副作用を隠すことがあります。サブクラスに対しては混乱させる可能性があります。
2.13.4決定
以前ならばシンプルで軽量なアクセサーまたはセッターメソッドを使用していたところに、新しいコードでは、プロパティを使ってデータにアクセスまたは設定します。プロパティは@property デコレータで作成する必要があります。
プロパティ自体がオーバーライドされていない場合、プロパティの継承は自明ではない可能性があります。したがって、サブクラスでオーバーライドされたメソッドがプロパティによって呼び出されるようにするために、アクセサメソッドが間接的に呼び出されるようにする必要があります( テンプレートメソッドデザインパターンを使用)。
Yes: import math
class Square:
"""A square with two properties: a writable area and a read-only perimeter.
To use:
>>> sq = Square(3)
>>> sq.area
9
>>> sq.perimeter
12
>>> sq.area = 16
>>> sq.side
4
>>> sq.perimeter
16
"""
def __init__(self, side):
self.side = side
@property
def area(self):
"""Area of the square."""
return self._get_area()
@area.setter
def area(self, area):
return self._set_area(area)
def _get_area(self):
"""Indirect accessor to calculate the 'area' property."""
return self.side ** 2
def _set_area(self, area):
"""Indirect setter to set the 'area' property."""
self.side = math.sqrt(area)
@property
def perimeter(self):
return self.side * 4
できる限り、「暗黙の」falseを使用してください。
2.14.1定義
Pythonは、ブールコンテキストでは、特定の値をFalse と評価します。簡単な「経験則」では、すべての「空の」値はfalseと見なされます。このため0, None, [], {}, ''は全て、ブールコンテキストではfalseと評価されます。
2.14.2長所
Pythonブール値を使用する条件は読みやすく、エラーが発生しにくくなります。ほとんどの場合、それらは高速でもあります。
2.14.3短所
C / C ++開発者には奇妙に見えるかもしれません。
2.14.4決定
可能であれば、「暗黙の」falseを使用します。たとえば、if foo != []:ではなくif foo: を。ただし、覚えておく必要のある注意事項がいくつかあります。
None値を確認するには、常にif foo is None:(またはis not None)を使用してください。たとえば、デフォルトでNone に設定されている変数または引数が他の値に設定されているかどうかをテストする場合。それ以外の値で、ブールコンテキストではfalseの値になる可能性がある値があります。
決して、ブール変数を== を使ってFalseと比較しないでください。代わりにif not x: を使用してください。あなたがFalseをNoneと区別する必要がある場合、if not x and x is not None: などと、式をチェーンしてください。
シーケンス(文字列、リスト、タプル)の場合、空のシーケンスはfalseであるという事実を使用してください。このためif seq:、およびif not seq:はそれぞれif len(seq): およびif not len(seq):よりも優先されます。
整数を処理する場合、暗黙のfalseは、利益よりもリスクが高くなる可能性があります(つまり、誤ってNone を0として処理する)。整数であることがわかっている(そしてlen()の結果ではない)値を整数0と比較するのが良いでしょう。
Yes: if not users:
print('no users')
if foo == 0:
self.handle_zero()
if i % 10 == 0:
self.handle_multiple_of_ten()
def f(x=None):
if x is None:
x = []
No: if len(users) == 0:
print('no users')
if foo is not None and not foo:
self.handle_zero()
if not i % 10:
self.handle_multiple_of_ten()
def f(x=None):
x = x or []
'0'(つまり、文字列としての0)はtrueと評価されることに注意してください。
可能な場合は、stringモジュールの代わりに文字列メソッドを使用してください。applyの代わりに関数呼び出し構文を使用してください。結局、関数の引数がインラインラムダになる場合、filterとmapの代わりに、リストの内包表記とforループを使用してください。reduceの代わりにforループを使用します。
2.15.1定義
Pythonの現在のバージョンは、一般的に好ましいと思われる代替構造を提供します。
2.15.2決定
これらの機能をサポートしていないPythonバージョンは使用していないため、新しいスタイルを使用しない理由はありません。
Yes: words = foo.split(':')
[x[1] for x in my_list if x[2] == 5]
map(math.sqrt, data) # Ok. No inlined lambda expression.
fn(*args, **kwargs)
No: words = string.split(foo, ':')
map(lambda x: x[1], filter(lambda x: x[2] == 5, my_list))
apply(fn, args, kwargs)
使用しても大丈夫です。
2.16.1定義
ネストされたPython関数は、囲んでいる関数で定義された変数を参照できますが、それらに代入することはできません。変数バインディングは、レキシカルスコープを使用して、つまり静的プログラムテキストに基づいて解決されます。Pythonは、ブロック内の名前への代入を全て、その名前へのすべての参照をローカル変数として扱います。使用が代入の前にある場合でもです。global 宣言が発生した場合、その名前はグローバル変数として扱われます。
この機能の使用例は次のとおりです。
def get_adder(summand1):
"""Returns a function that adds numbers to a given number."""
def adder(summand2):
return summand1 + summand2
return adder
2.16.2長所
多くの場合、より明確でエレガントなコードになります。特に経験豊富なLispとScheme(そしてHaskellとMLと…)プログラマーにとっては安心です。
2.16.3短所
紛らわしいバグにつながる可能性があります。PEP-0227に基づくこの例のように :
i = 4
def foo(x):
def bar():
print(i, end='')
# ...
# A bunch of code here
# ...
for i in x: # Ah, i *is* local to foo, so this is what bar sees
print(i, end='')
bar()
したがって、foo([1, 2, 3])は、1 2 3 4ではなく、1 2 3 3を印刷します。
2.16.4決定
使用しても大丈夫です。
明らかな利点がある場合は、デコレータを慎重に使用してください。staticmethod を避け、classmethodの使用を制限します。
2.17.1定義
関数とメソッドのデコレータ (別名「@表記法」)。一般的なデコレータの1つは@propertyで、通常のメソッドを動的に計算される属性に変換するために使用されます。ただし、デコレータ構文では、ユーザー定義のデコレータも使用できます。具体的には、ある関数my_decoratorの場合、これは:
class C:
@my_decorator
def method(self):
# method body ...
と同等です。
class C:
def method(self):
# method body ...
method = my_decorator(method)
2.17.2長所
メソッドの変換をエレガントに指定します。変換により、反復的なコードが削除されたり、不変条件が適用されたりする場合があります。
2.17.3短所
デコレータは、関数の引数または戻り値に対して任意の操作を実行できるため、驚くべき暗黙の動作が発生します。さらに、デコレータはインポート時に実行されます。デコレータコードの障害から回復することはほとんど不可能です。
2.17.4決定
明らかな利点がある場合は、デコレータを慎重に使用してください。デコレータは、関数と同じインポートおよび命名ガイドラインに従う必要があります。デコレータpydocは、関数がデコレータであることを明確に示す必要があります。デコレータの単体テストを作成します。
デコレータ自体の外部依存関係(たとえば、ファイル、ソケット、データベース接続などに依存しないこと)は、デコレータの実行時に(インポート時に。もしかすると、pydocまたは他のツールからの)利用できない可能性があるため、避けてください。有効なパラメータで呼び出されるデコレータは、(可能な限り)すべての場合に成功することが保証されている必要があります。
デコレータは「トップレベルコード」の特殊なケースです。詳細については、mainを参照してください。
既存のライブラリで定義されているAPIと統合するために強制されない限り、staticmethodを使用しないでください。代わりに、モジュールレベルの関数を記述してください。
名前付きコンストラクター、またはプロセスワイドのキャッシュのような必要なグローバル状態を変更するクラス固有のルーチンを作成する場合にのみclassmethod を使用します。
組み込み型の原子性に依存しないでください。
Pythonの組み込みの辞書などのデータ型は、アトミック操作を持っているように見えますが、それらがアトミックでなく、そのアトミック性に依拠すべきでないコーナーケースがあります(例えば__hash__ や__eq__がPythonのメソッドとして実装されている場合)。また、アトミックな変数の代入に依存するべきではありません(これは辞書に依存するため)。
スレッド間でデータを通信するための推奨される方法として、キューモジュールのQueueデータ型を使用します。それ以外の場合は、スレッドモジュールとそのロックプリミティブを使用します。低レベルのロックを使用する代わりに、条件変数とthreading.Conditionを優先します。
これらの機能は避けてください。
2.19.1定義
Pythonは非常に柔軟な言語であり、カスタムメタクラス、バイトコードへのアクセス、オンザフライコンパイル、動的継承、オブジェクトの親の変更、インポートハック、リフレクション(例:getattr()の一部の使用)、システム内部の変更など、多くのファンシーな機能を提供します 。
2.19.2長所
これらは強力な言語機能です。コードをよりコンパクトにすることができます。
2.19.3短所
絶対に必要ではないときでも、これらの「クールな」機能を使用することは非常に魅力的です。隠された異常な機能を使用しているコードを読んだり、理解したり、デバッグしたりするのは困難です。最初は(元の作成者には)そのようには見えませんが、コードを再検討すると、長いが単純なコードよりも難しい傾向があります。
2.19.4決定
コードでこれらの機能を避けてください。
内部的にこれらの機能を使用する標準ライブラリモジュールとクラスを使用しても大丈夫です(例えば、abc.ABCMeta、collections.namedtuple、dataclasses、および enum)。
Python 3が登場!すべてのプロジェクトがまだそれを使用する準備ができているわけではありませんが、すべてのコードは3互換になるように作成する必要があります(可能な場合は3でテストします)。
2.20.1定義
Python 3は、Python言語の重要な変更です。既存のコードは2.7を念頭に置いて記述されることがよくありますが、コードをその意図についてより明確にし、Python3で変更せずに使用できるように準備するための簡単な方法がいくつかあります。
2.20.2長所
Python 3を念頭に置いて記述されたコードは、プロジェクトのすべての依存関係の準備ができたら、Python 3でより明確で、実行しやすくなります。
2.20.3短所
一部の人々は、追加の定型文が醜いと感じています。インポートによって追加された機能を実際に必要としないモジュールにインポートを追加することは珍しいことです。
2.20.4決定
from __future__ imports
from __future__ importステートメントの使用をお勧めします。すべての新しいコードには次のものが含まれている必要があり、可能であれば既存のコードを更新して互換性を持たせる必要があります。
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
これらのインポートの詳細については、絶対インポート、 / 除算動作、および print機能を参照してください 。
コードがPython3のみである場合を除き、現在モジュールで使用されていない場合でも、これらのインポートを省略または削除しないでください。誰かがそのような機能を使い始めた後の編集中にそれらが忘れられないように、すべてのファイルに将来のインポートを常に持っている方が良いです。
他のfrom __future__ importsステートメントがあります。必要に応じて使用してください。unicode_literals は、Python 2.7内の多くの場所で暗黙のデフォルトコーデック変換の結果が発生するため、明確な勝利ではないため、推奨事項には含めません。ほとんどのコードは、明示的な b''と、 u'' を使用したバイトとUnicode文字列リテラルを使った方が、良いでしょう。
six, future そして past ライブラリ
プロジェクトでPython2と3の両方での使用を積極的にサポートする必要がある場合は、適切と思われるならば、six, future、および pastライブラリを使用してください。これらは、コードをよりクリーンで簡単にするために存在します。
PEP-484に従ってPython3コードに注釈を付けてタイプヒントを与え、pytypeなどのタイプチェックツールを使用してビルド時にコードをタイプチェックできます。
型注釈は、ソースまたは スタブpyiファイルに含めることができます。可能な限り、注釈はソースに含める必要があります。サードパーティまたは拡張モジュールにはpyiファイルを使用します。
2.21.1定義
型注釈(または「型ヒント」)は、関数またはメソッドの引数と戻り値のためです。
def func(a: int) -> List[int]:
同様にPEP-526構文を使用して、変数の型を宣言することもでき ます。
a: SomeType = some_func()
または、レガシーPythonバージョンをサポートする必要があるコードでタイプコメントを使用します。
a = some_func() # type: SomeType
2.21.2長所
型注釈により、コードの可読性と保守性が向上します。タイプチェッカーは、多くのランタイムエラーをビルド時エラーに変換し、パワー機能を使用する能力を低下させます。
2.21.3短所
型宣言を最新の状態に保つ必要があります。有効なコードと思われるタイプエラーが表示される場合があります。タイプチェッカーを使用すると、 パワー機能 を使用する能力が低下する場合があります。
2.21.4決定
コードを更新するときは、Python型分析を有効にすることを強くお勧めします。パブリックAPIを追加または変更するときは、タイプアノテーションを含め、ビルドシステムでpytypeを介したチェックを有効にします。静的分析はPythonにとって比較的新しいため、望ましくない副作用(誤って推測された型など)が一部のプロジェクトでの採用を妨げる可能性があることを認識しています。そのような状況では、作成者はTODOを含むコメントを追加するか、BUILDファイルまたはコード自体で現在タイプアノテーションの採用を妨げている問題を説明するバグへのリンクを適宜追加することをお勧めします。
行をセミコロンで終了したり、セミコロンを使用して2つのステートメントを同じ行に配置したりしないでください。
最大行長は80文字です。
80文字の制限に対する明示的な例外:
長いインポートステートメント。
コメント内のURL、パス名、または長いフラグ。
URLやパス名など、行に分割するのに不便な空白を含まない長いモジュールレベル文字列定数。
Pylintでの無効にするコメント(例:# pylint: disable=invalid-name)
3つ以上のコンテキスト・マネージャーを必要とするwithステートメントを除いて、バックスラッシュの継続行を使用しないでください。
かっこ、角かっこ、中かっこ内でのPythonの暗黙的な行結合を利用 します。必要に応じて、式の前後に括弧のペアを追加できます。
Yes: foo_bar(self, width, height, color='black', design=None, x='foo',
emphasis=None, highlight=0)
if (width == 0 and height == 0 and
color == 'red' and emphasis == 'strong'):
リテラル文字列が1行に収まらない場合は、暗黙の行結合に括弧を使用します。
x = ('This will build a very long long '
'long long long long long long string')
コメント内で、必要に応じて長いURLを独自の行に配置します。
Yes: # See details at
# http://www.example.com/us/developer/documentation/api/content/v2.0/csv_file_name_extension_full_specification.html
No: # See details at
# http://www.example.com/us/developer/documentation/api/content/\
# v2.0/csv_file_name_extension_full_specification.html
式が3行以上にまたがるwithステートメントを定義する場合は、バックスラッシュの継続行を使用できます。2行の式には、ネストされたwithステートメントを使用します。
Yes: with very_long_first_expression_function() as spam, \
very_long_second_expression_function() as beans, \
third_thing() as eggs:
place_order(eggs, beans, spam, beans)
No: with VeryLongFirstExpressionFunction() as spam, \
VeryLongSecondExpressionFunction() as beans:
PlaceOrder(eggs, beans, spam, beans)
Yes: with very_long_first_expression_function() as spam:
with very_long_second_expression_function() as beans:
place_order(beans, spam)
上記の行継続の例の要素のインデントに注意してください。説明については、インデントのセクションを参照してください。
他のすべての場合 に、行が80文字を超え、yapf 自動フォーマッタが行を制限未満にするのに役立たない場合、行はこの最大値を超えることができます。
括弧は慎重に使用してください。
必須ではありませんが、タプルの前後に括弧を使用することは問題ありません。暗黙の行継続またはタプルを示すために括弧を使用するのでない限り、returnステートメントまたは条件ステートメントでそれらを使用しないでください。
Yes: if foo:
bar()
while x:
x = bar()
if x and y:
bar()
if not x:
bar()
# For a 1 item tuple the ()s are more visually obvious than the comma.
onesie = (foo,)
return foo
return spam, beans
return (spam, beans)
for (x, y) in dict.items(): ...
No: if (x):
bar()
if not(x):
bar()
return (foo)
コードブロックを4つのスペースでインデントします。
タブを使用したり、タブとスペースを混在させたりしないでください。暗黙の行継続の場合は、行の長さのセクションの例のように、ラップされた要素を垂直方向に揃える必要があります 。または、4つのスペースのぶら下げインデントを使用します。この場合、最初の行の開き括弧または角かっこの後には何もあってはいけません。
Yes: # Aligned with opening delimiter
foo = long_function_name(var_one, var_two,
var_three, var_four)
meal = (spam,
beans)
# Aligned with opening delimiter in a dictionary
foo = {
long_dictionary_key: value1 +
value2,
...
}
# 4-space hanging indent; nothing on first line
foo = long_function_name(
var_one, var_two, var_three,
var_four)
meal = (
spam,
beans)
# 4-space hanging indent in a dictionary
foo = {
long_dictionary_key:
long_dictionary_value,
...
}
No: # Stuff on first line forbidden
foo = long_function_name(var_one, var_two,
var_three, var_four)
meal = (spam,
beans)
# 2-space hanging indent forbidden
foo = long_function_name(
var_one, var_two, var_three,
var_four)
# No hanging indent in a dictionary
foo = {
long_dictionary_key:
long_dictionary_value,
...
}
項目のシーケンスの末尾のカンマは、閉じるコンテナトークン ]、)または} が、最後の要素と同じ行にあらわれない場合にだけ、推奨されます。末尾のコンマの存在は、Pythonコード自動フォーマッターYAPFへのヒントとしても使用され,、最後の要素の後に , が存在する場合に、アイテムのコンテナーを1行に1つのアイテムに自動フォーマットするように指示します。
Yes: golomb3 = [0, 1, 3]
Yes: golomb4 = [
0,
1,
4,
6,
]
No: golomb4 = [
0,
1,
4,
6
]
関数定義またはクラス定義のトップレベル定義間には、2つの空白行。メソッド定義間と、class 行と最初のメソッドの間には、1行の空白行。def行の後に空白行はありません。関数またはメソッド内で適切と判断する場合は、空白行を1行使用してください。
句読点の前後のスペースの使用については、標準の活版印刷規則に従ってください。
括弧、角かっこ、中括弧の内側に空白はありません。
Yes: spam(ham[1], {eggs: 2}, [])
No: spam( ham[ 1 ], { eggs: 2 }, [ ] )
コンマ、セミコロン、またはコロンの前に空白はありません。行の終わりを除いて、コンマ、セミコロン、またはコロンの後に空白を使用してください。
Yes: if x == 4:
print(x, y)
x, y = y, x
No: if x == 4 :
print(x , y)
x , y = y , x
引数リスト、インデックス付け、またはスライスを開始するオープン括弧/ブラケットの前に空白はありません。
Yes: spam(1)
No: spam (1)
Yes: dict['key'] = list[index]
No: dict ['key'] = list [index]
末尾に空白はありません。
代入(=)、比較(==, <, >, !=, <>, <=, >=, in, not in, is, is not)、およびブール値(and, or, not)のために、両側を1つのスペースで二項演算子を囲みます。算術演算子(+、-、*、/、//、%、**、@)の周りにスペースを挿入するかは、あなたのより良い判断を使用してください。
Yes: x == 1
No: x<1
キーワード引数を渡すときとデフォルトのパラメータ値を定義するときには、一つの例外を除いて、= の周りにスペースを使用しないでください: 型注釈が存在する場合、デフォルトのパラメータ値のためには、必ず、=の周りに空白を使ってください。
Yes: def complex(real, imag=0.0): return Magic(r=real, i=imag)
Yes: def complex(real, imag: float = 0.0): return Magic(r=real, i=imag)
No: def complex(real, imag = 0.0): return Magic(r = real, i = imag)
No: def complex(real, imag: float=0.0): return Magic(r = real, i = imag)
それは、メンテナンスの負担になりますので、連続したライン上でトークンを垂直整列するためにスペースを使用しないでください(:、#、=、などに適用される):
Yes:
foo = 1000 # comment
long_name = 2 # comment that should not be aligned
dictionary = {
'foo': 1,
'long_name': 2,
}
No:
foo = 1000 # comment
long_name = 2 # comment that should not be aligned
dictionary = {
'foo' : 1,
'long_name': 2,
}
ほとんどの.pyファイルは#!行で始める必要はありません。PEP-394に従って#!/usr/bin/pythonに、オプションの1桁の2または3サフィックスを 使用してプログラムのメインファイルを開始します 。
この行は、カーネルがPythonインタープリターを見つけるために使用しますが、モジュールをインポートするときにPythonによって無視されます。直接実行されるファイルでのみ必要です。
モジュール、関数、メソッドdocstring、およびインラインコメントには必ず正しいスタイルを使用してください。
3.8.1Docstrings
Pythonはdocstringを使用してコードを文書化します。docstringは、パッケージ、モジュール、クラス、または関数の最初のステートメントである文字列です。これらの文字列は、オブジェクトの__doc__メンバーを介して自動的に抽出でき、pydocによって使用されます。(あなたのモジュールでpydocを実行して、どのように表示されるかを確認してください。)docstringには常に3つの二重引用符形式"""を使用します(PEP 257に準拠 )。docstringは、ピリオド、疑問符、または感嘆符で終わる要約行(80文字を超えない1つの物理行)として編成する必要があります。さらに書く場合(推奨)、この後に空白行が続き、その後に最初の行の最初の引用符と同じカーソル位置から始まる残りのdocstringが続く必要があります。以下に、docstringのフォーマットガイドラインがあります。
3.8.2モジュール
すべてのファイルには、ライセンスの定型文が含まれている必要があります。プロジェクトで使用されるライセンスに適切な定型文を選択します(たとえば、Apache 2.0、BSD、LGPL、GPL)
ファイルは、モジュールの内容と使用法を説明するdocstringで始まる必要があります。
"""A one line summary of the module or program, terminated by a period.
Leave one blank line. The rest of this docstring should contain an
overall description of the module or program. Optionally, it may also
contain a brief description of exported classes and functions and/or usage
examples.
Typical usage example:
foo = ClassFoo()
bar = foo.FunctionBar()
"""
3.8.3関数とメソッド
このセクションでは、「関数」とは、メソッド、関数、またはジェネレーターを意味します。
次のすべての基準を満たさない限り、関数にはdocstringが必要です。
外部からは見えない
とても短い
明らか
docstringは、関数のコードを読まずに関数の呼び出しを書くのに十分な情報を提供する必要があります。docstringは、属性と同じスタイルを使用する必要がある@propertyデータ記述子を除いて、命令型("""Fetch rows from a Bigtable.""")ではなく記述型("""Fetches rows from a Bigtable.""")である必要があります。docstringは、実装ではなく、関数の呼び出し構文とそのセマンティクスを記述する必要があります。トリッキーなコードの場合、docstringを使用するよりも、コードの横にコメントを付ける方が適切です。
基本クラスのメソッドをオーバーライドするメソッドでは、オーバーライドされたメソッドのdocstringにを送る単純なdocstring("""See base class."""。など)があってもよいです。理論的根拠は、基本メソッドのdocstringにすでに存在するドキュメントを多くの場所で繰り返す必要がないということです。ただし、オーバーライドするメソッドの動作がオーバーライドされるメソッドと大幅に異なる場合、または詳細を提供する必要がある場合(たとえば、追加の副作用を文書化する)、オーバーライドするメソッドには少なくともそれらの違いを含むdocstringが必要です。
関数の特定の側面は、以下にリストされている特別なセクションに文書化する必要があります。各セクションは見出し行で始まり、コロンで終わります。見出し以外のすべてのセクションでは、2つまたは4つのスペースのぶら下げインデントを維持する必要があります(ファイル内で一貫性を保つ)。関数の名前とシグネチャが十分に有益であり、1行のdocstringを使用して適切に記述できる場合は、これらのセクションを省略できます。
各パラメーターを名前でリストします。説明は名前の後に続き、コロンとそれに続くスペースまたは改行で区切る必要があります。説明が長すぎて1行の80文字に収まらない場合は、パラメーター名より2〜4スペース大きいぶら下げインデントを使用します(ファイル内の残りのdocstringと一致させてください)。コードに対応する型注釈が含まれていない場合は、説明に必要な型を含める必要があります。関数が*foo(可変長引数リスト)および/または**bar (任意のキーワード引数)を受け入れる場合、それらは*fooおよび**barとしてリストする必要があります。
Returns: (or Yields: for generators)
戻り値のタイプとセマンティクスを説明します。関数がNoneのみを返す場合、このセクションは必要ありません。docstringがReturnsまたはYields(eg """Returns row from Bigtable as a tuple of strings.""")で始まり、最初の文が戻り値を説明するのに十分である場合も、省略できます。
Raises:
インターフェイスに関連するすべての例外をリストし、その後に説明を続けます。Args:で説明されているように、同様の例外名+コロン+スペースまたは改行とぶら下げインデントスタイルを使用します。docstringで指定されたAPIに違反した場合に発生する例外を文書化しないでください(逆説的に、APIに違反する動作を API の部分とするため)。
def fetch_smalltable_rows(table_handle: smalltable.Table,
keys: Sequence[Union[bytes, str]],
require_all_keys: bool = False,
) -> Mapping[bytes, Tuple[str]]:
"""Fetches rows from a Smalltable.
Retrieves rows pertaining to the given keys from the Table instance
represented by table_handle. String keys will be UTF-8 encoded.
Args:
table_handle: An open smalltable.Table instance.
keys: A sequence of strings representing the key of each table
row to fetch. String keys will be UTF-8 encoded.
require_all_keys: Optional; If require_all_keys is True only
rows with values set for all keys will be returned.
Returns:
A dict mapping keys to the corresponding table row data
fetched. Each row is represented as a tuple of strings. For
example:
{b'Serak': ('Rigel VII', 'Preparer'),
b'Zim': ('Irk', 'Invader'),
b'Lrrr': ('Omicron Persei 8', 'Emperor')}
Returned keys are always bytes. If a key from the keys argument is
missing from the dictionary, then that row was not found in the
table (and require_all_keys must have been False).
Raises:
IOError: An error occurred accessing the smalltable.
"""
同様に、改行を使用したこのArgs: のバリエーションも許可されます。
def fetch_smalltable_rows(table_handle: smalltable.Table,
keys: Sequence[Union[bytes, str]],
require_all_keys: bool = False,
) -> Mapping[bytes, Tuple[str]]:
"""Fetches rows from a Smalltable.
Retrieves rows pertaining to the given keys from the Table instance
represented by table_handle. String keys will be UTF-8 encoded.
Args:
table_handle:
An open smalltable.Table instance.
keys:
A sequence of strings representing the key of each table row to
fetch. String keys will be UTF-8 encoded.
require_all_keys:
Optional; If require_all_keys is True only rows with values set
for all keys will be returned.
Returns:
A dict mapping keys to the corresponding table row data
fetched. Each row is represented as a tuple of strings. For
example:
{b'Serak': ('Rigel VII', 'Preparer'),
b'Zim': ('Irk', 'Invader'),
b'Lrrr': ('Omicron Persei 8', 'Emperor')}
Returned keys are always bytes. If a key from the keys argument is
missing from the dictionary, then that row was not found in the
table (and require_all_keys must have been False).
Raises:
IOError: An error occurred accessing the smalltable.
"""
3.8.4クラス
クラスには、クラス定義の下にクラスを説明するdocstringが必要です。クラスにパブリック属性がある場合、それらはここのAttributesセクションに文書化され、関数のArgsセクションと同じフォーマットに従う 必要があります 。
class SampleClass:
"""Summary of class here.
Longer class information....
Longer class information....
Attributes:
likes_spam: A boolean indicating if we like SPAM or not.
eggs: An integer count of the eggs we have laid.
"""
def __init__(self, likes_spam=False):
"""Inits SampleClass with blah."""
self.likes_spam = likes_spam
self.eggs = 0
def public_method(self):
"""Performs operation blah."""
3.8.5ブロックコメントとインラインコメント
コメントを付ける最後の場所は、コードのトリッキーな部分です。次のコードレビューで説明する必要がある場合は、今すぐコメントする必要があります。複雑な操作は、操作が開始される前に数行のコメントを受け取ります。自明でないものは、行の終わりにコメントを受け取ります。
# We use a weighted dictionary search to find out where i is in
# the array. We extrapolate position based on the largest num
# in the array and the array size and then do binary search to
# get the exact number.
if i & (i-1) == 0: # True if i is 0 or a power of 2.
読みやすさを向上させるために、これらのコメントは、コメント文字#でコードから少なくとも2スペース離れて開始し、コメント自体のテキストの前に少なくとも1スペースが続く必要があります。
一方、コードを記述しないでください。コードを読んでいる人は、Pythonを(あなたがやろうとしていることは知らなくても)あなたよりもよく知っていると仮定しなさい。
# BAD COMMENT: Now go through the b array and make sure whenever i occurs
# the next element is i+1
3.8.6句読点、スペル、および文法
句読点、スペル、および文法に注意してください。よく書かれたコメントは、よく書かれていないコメントよりも読みやすいです。
コメントは、適切な大文字と句読点を使用して、説明テキストと同じくらい読みやすくする必要があります。多くの場合、完全な文は文の断片よりも読みやすくなります。コード行の最後のコメントなど、短いコメントは形式的でない場合がありますが、スタイルに一貫性を持たせる必要があります。
セミコロンを使用する必要があるときにコンマを使用していることをコードレビューアに指摘されるのはイライラするかもしれませんが、ソースコードが高レベルの明瞭さと読みやすさを維持することは非常に重要です。適切な句読点、スペル、および文法は、その目標に役立ちます。
クラスは明示的にobjectから継承する必要はありません(Python 2との互換性がない限り)。
Modern:
class SampleClass:
pass
class OuterClass:
class InnerClass:
pass
Ancient:
class SampleClass(object):
pass
class OuterClass(object):
class InnerClass(object):
pass
パラメータがすべて文字列の場合でも、formatメソッドまたは%演算子を使用して文字列をフォーマットします。ただし、+と%(またはformat)の間で 決定するには、最善の判断を使用してください。
Yes: x = a + b
x = '%s, %s!' % (imperative, expletive)
x = '{}, {}'.format(first, second)
x = 'name: %s; score: %d' % (name, n)
x = 'name: {}; score: {}'.format(name, n)
x = f'name: {name}; score: {n}' # Python 3.6+
No: x = '%s%s' % (a, b) # use + in this case
x = '{}{}'.format(a, b) # use + in this case
x = first + ', ' + second
x = 'name: ' + name + '; score: ' + str(n)
ループ内で文字列を累積するために+と+=演算子を使用することは避けてください。文字列は不変であるため、これにより不要な一時オブジェクトが作成され、実行時間が線形ではなく2次になります。代わりに、各部分文字列をリストに追加し、ループが終了した後に''.joinします(または、各部分文字列をio.BytesIOバッファーに書き込みます)。
Yes: items = ['<table>']
for last_name, first_name in employee_list:
items.append('<tr><td>%s, %s</td></tr>' % (last_name, first_name))
items.append('</table>')
employee_table = ''.join(items)
No: employee_table = '<table>'
for last_name, first_name in employee_list:
employee_table += '<tr><td>%s, %s</td></tr>' % (last_name, first_name)
employee_table += '</table>'
ファイル内の文字列引用文字の選択を一貫させてください。' か"を選び、それに固執します。\\ 文字列内でエスケープする必要がないように、文字列で他の引用文字を使用してもかまいません。
Yes:
Python('Why are you hiding your eyes?')
Gollum("I'm scared of lint errors.")
Narrator('"Good!" thought a happy Python reviewer.')
No:
Python("Why are you hiding your eyes?")
Gollum('The lint. It burns. It burns us.')
Gollum("Always the great lint. Watching. Watching.")
複数行の文字列では、'''よりも"""を優先します。プロジェクトは、通常の文字列に ' を使用する場合に限り、すべての非docstring複数行文字列 に'''を使用することを選択できます。Docstringはそれには関係なく"""を使用する必要があります。
複数行の文字列は、プログラムの残りの部分のインデントと一緒にはなりません。文字列に余分なスペースを埋め込まないようにする必要がある場合は、連結された単一行の文字列または複数行の文字列にtextwrap.dedent() を使用 して、各行の最初のスペースを削除します。
No:
long_string = """This is pretty ugly.
Don't do this.
"""
Yes:
long_string = """This is fine if your use case can accept
extraneous leading spaces."""
Yes:
long_string = ("And this is fine if you cannot accept\n" +
"extraneous leading spaces.")
Yes:
long_string = ("And this too is fine if you cannot accept\n"
"extraneous leading spaces.")
Yes:
import textwrap
long_string = textwrap.dedent("""\
This is also fine, because textwrap.dedent()
will collapse common leading spaces in each line.""")
ファイルとソケットを使い終わったら、それらを明示的に閉じます。
ファイル、ソケット、またはその他のファイルのようなオブジェクトを不必要に開いたままにしておくと、多くの欠点があります。
それらは、ファイル記述子などの限られたシステムリソースを消費する可能性があります。このようなオブジェクトの多くを処理するコードは、使用後すぐにシステムに戻されない場合、これらのリソースを不必要に使い果たす可能性があります。
ファイルを開いたままにしておくと、ファイルの移動や削除などの他のアクションが妨げられる可能性があります。
プログラム全体で共有されているファイルとソケットは、論理的に閉じられた後、誤って読み取りまたは書き込みが行われる可能性があります。それらが実際に閉じられている場合、それらからの読み取りまたは書き込みを試みると例外がスローされ、問題がより早く認識されます。
また、ファイルオブジェクトが破棄されると、ファイルとソケットは自動的に閉じられますが、ファイルオブジェクトの存続期間をファイルの状態に関連付けることはお勧めできません。
ランタイムが実際にファイルのデストラクタを実行する時期についての保証はありません。異なるPython実装は、遅延ガベージコレクションなど、異なるメモリ管理手法を使用します。これにより、オブジェクトの寿命が任意に無期限に長くなる可能性があります。
グローバルや例外トレースバックなどでのファイルへの予期しない参照により、意図したよりも長くファイルが保持される場合があります。
ファイルを管理するための好ましい方法は、次のwithステートメントを使用することです 。
with open("hello.txt") as hello_file:
for line in hello_file:
print(line)
withステートメントをサポートしないファイルのようなオブジェクトの場合は、contextlib.closing()を使用します 。
import contextlib
with contextlib.closing(urllib.urlopen("http://www.python.org/")) as front_page:
for line in front_page:
print(line)
一時的、短期的な解決策、または十分であるが完全ではないコードにはTODOコメントを使用します。
TODOコメントは、すべて大文字のTODO文字列で始まり、問題についての最もよい文脈を持つ人の名前、電子メールアドレスや他の識別子または、issueを括弧に入れて続けます。続いて、何をすべきかについて説明します。
目的は、詳細を取得する方法を見つけるために検索できる一貫したTODO形式を用意することです。TODOは、参照された人が問題を修正するという約束ではありません。したがって、 TODOを作成するときに与えられるのは、ほとんどの場合、あなたの名前です。
# TODO(kl@gmail.com): Use a "*" here for string repetition.
# TODO(Zeke) Change this to use relations.
あなたのTODOが「未来の日付では、何かをする」の形式である場合、あなたは非常に特定の日付(「2009年11月までに修正」)を入れるか、または非常に特定のイベントを入れることを確認してください(「すべてのクライアントがXML応答を処理することができたときにこのコードを削除してください。 」)。
インポートは別々の行に行う必要があります。typingインポートには例外が あります。
例えば:
Yes: import os
import sys
from typing import Mapping, Sequence
No: import os, sys
インポートは常にファイルの先頭、モジュールのコメントとdocstringの直後、モジュールのグローバル変数と定数の前に配置されます。インポートは、最も一般的なものから最も一般的でないものへとグループ化する必要があります。
Pythonの将来のインポートステートメント。例えば:
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
それらの詳細については、上記を参照してください。
Python標準ライブラリのインポート。例えば:
import sys
サードパーティのモジュールまたはパッケージのインポート。例えば:
import tensorflow as tf
コードリポジトリサブパッケージのインポート。例えば:
from otherproject.ai import mind
非推奨:このファイルと同じトップレベルのサブパッケージの一部であるアプリケーション固有のインポート。例えば:
from myproject.backend.hgwells import time_machine
古いGooglePythonスタイルのコードがこれを実行しているのを見つけるかもしれませんが、それはもはや必要ではありません。新しいコードはこれを気にしないことをお勧めします。アプリケーション固有のサブパッケージのインポートを他のサブパッケージのインポートと同じように扱うだけです。
各グループ内で、インポートは、各モジュールの完全なパッケージパス(path in from path 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
# Older style code may have these imports down here instead:
#from myproject.backend.hgwells import time_machine
#from myproject.backend.state_machine import main_loop
通常、1行に1つのステートメントのみ。
ただし、ステートメント全体が1行に収まる場合にのみ、テストの結果をテストと同じ行に配置できます。特に、try/exceptであなたがそうすることはできません 。tryとexceptは同じライン上に両方がフィットすることはできません。 else が無い if でだけ、それが可能です。
Yes:
if foo: bar(foo)
No:
if foo: bar(foo)
else: baz(foo)
try: bar(foo)
except ValueError: baz(foo)
try:
bar(foo)
except ValueError: baz(foo)
アクセサ関数が簡単な場合は、アクセサ関数の代わりにパブリック変数を使用して、Pythonでの関数呼び出しの余分なコストを回避する必要があります。さらに機能が追加されるとpropertyを、構文の一貫性を保つために使用できます。
一方、アクセスがより複雑な場合、または変数へのアクセスのコストが大きい場合は、get_foo()やset_foo()などの関数呼び出し(命名ガイドラインに従う)を使用する必要が あります。過去の動作でプロパティを介したアクセスが許可されていた場合は、新しいアクセサ関数をプロパティにバインドしないでください。古い方法で変数にアクセスしようとしているコードは、複雑さの変化を認識できるように、目に見えるように壊れるべきです。
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ファイル名拡張子を使用してください。ダッシュは絶対に使用しないでください。
3.16.1避けるべき名前
特に許可されている場合を除いて、単一文字の名前:
カウンタまたはイテレータ(例えばi、j、k、v、ら。)
try/exceptステートメントの例外識別子としてe。
withステートメントのファイルハンドルとしてf
1文字の命名を乱用しないように注意してください。一般的に言えば、説明性は名前の可視性の範囲に比例する必要があります。たとえば i は、5行のコードブロックの名前としては適切かもしれませんが、ネストされた複数のスコープ内では、あいまいすぎる可能性があります。
任意のパッケージ/モジュール名のダッシュ(-)
__double_leading_and_trailing_underscore__ 名前(Pythonで予約済み)
不快な用語
3.16.2命名規則
「内部」とは、モジュールの内部、またはクラス内で保護またはプライベートを意味します。
単一の下線(_)を前に付けると、モジュール変数と関数を保護するためのサポートがいくつかあります(リンターは保護されたメンバーアクセスにフラグを立てます)。インスタンス変数またはメソッドの前に二重アンダースコア(__別名「dunder」)を付けると、変数またはメソッドがそのクラスに対してプライベートになります(名前マングリングを使用)が、読みやすさとテスト容易性に影響を与え、実際にはプライベートではないため、使用をお勧めしません。
関連するクラスとトップレベルの関数を一緒にモジュールに配置します。Javaとは異なり、モジュールごとに1つのクラスに制限する必要はありません。
クラス名にはCapWordsを使用しますが、モジュール名にはlower_with_under.pyを使用します。CapWords.pyという名前の古いモジュールがいくつかありますが、モジュールがクラスにちなんで名付けられたときに混乱するため、これは推奨されません。(「待てよ-私はimport StringIOかfrom StringIO import StringIOのどちらを書いた?」)
それらのコンポーネントがCapWordsを使用している場合でも、名前の論理コンポーネントを区切るためにtest始まるユニットテストメソッド名にアンダースコアが表示される場合があります。考えられるパターンの1つはtest<MethodUnderTest>_<state>;です。たとえばtestPop_EmptyStackは大丈夫です。テストメソッドに名前を付ける正しい方法は1つではありません。
3.16.3ファイルの命名
Pythonファイル名には.py拡張子を付ける必要があり、ダッシュ(-)を含めることはできません。これにより、それらをインポートしてユニットテストすることができます。拡張子なしで実行可能ファイルにアクセスできるようにする場合は、シンボリックリンクまたはexec "$0.py" "$@"を含む単純なbashラッパーを使用します。
3.16.4グイドさんの提言に由来するガイドライン
Pythonでは、pydoc や単体テストは、モジュールがインポート可能である必要があります。ファイルを実行可能ファイルとして使用する場合、その主な機能はmain()関数内にある必要があり 、モジュールのインポート時に実行されないように、コードはメインプログラムを実行する前に常にif __name__ == '__main__'をチェックする必要があります。
abslを使用する場合は、app.runを使用します。
from absl import app
...
def main(argv):
# process non-flag arguments
...
if __name__ == '__main__':
app.run(main)
それ以外の場合は、次を使用します。
def main():
...
if __name__ == '__main__':
main()
モジュールがインポートされると、トップレベルのすべてのコードが実行されます。ファイルがpydocされたときに、関数の呼び出し、オブジェクトの作成、または実行してはならないその他の操作を実行しないように注意してください。
小さくて焦点を絞った関数を優先しなさい。
私達は、長い関数が適切な場合があることを認識しているため、関数の長さに厳しい制限はつけません。関数が約40行を超える場合は、プログラムの構造を損なうことなく分割できるかどうかを検討してください。
長い関数が今は完全に機能するとしても、数か月以内に誰かがそれを変更すると、新しい動作が追加される可能性があります。これにより、見つけるのが難しいバグが発生する可能性があります。関数を短くシンプルに保つことで、他の人がコードを読んだり変更したりしやすくなります。
一部のコードで作業をすると、長くて複雑な関数が見つかる可能性があります。既存のコードを変更することを恐れないでください。そのような関数の作業が難しい場合、エラーのデバッグが難しい場合、またはその一部をいくつかの異なるコンテキストで使用したい場合は、関数をより小さく扱いやすい部分に分割することを検討してください。
3.19.1一般規則
PEP-484をよく理解してください 。
メソッドでは、selfとclsには、適切な型情報が必要な場合にのみ注釈を付けます。例えば、@classmethod def create(cls: Type[T]) -> T: return cls()
他の変数または返される型を表現する必要がない場合は、Anyを使用します。
モジュール内のすべての関数に注釈を付ける必要はありません。
少なくともパブリックAPIに注釈を付けます。
判断を使用して、一方では安全性と明快さ、他方では柔軟性のバランスをとってください。
タイプ関連のエラー(以前のバグや複雑さ)が発生しやすいコードに注釈を付けます。
理解しにくいコードに注釈を付けます。
タイプの観点からコードが安定したら、コードに注釈を付けます。多くの場合、柔軟性をあまり失うことなく、成熟したコードのすべての関数に注釈を付けることができます。
3.19.2改行
既存のインデント規則に従うようにしてください。
注釈を付けると、多くの関数シグネチャが「1行に1つのパラメータ」になります。
def my_method(self,
first_var: int,
second_var: Foo,
third_var: Optional[Bar]) -> int:
...
常に、変数名と型注釈の間ではなく、変数間で改行することを優先しなさい。ただし、すべてが同じ行に収まる場合は、それを選択してください。
def my_method(self, first_var: int) -> int:
...
関数名、最後のパラメーター、および戻り値の型の組み合わせが長すぎる場合は、新しい行で4だけインデントします。
def my_method(
self, first_var: int) -> Tuple[MyLongType1, MyLongType1]:
...
戻り値の型が最後のパラメーターと同じ行に収まらない場合、推奨される方法は、新しい行でパラメーターを4だけインデントし、閉じ括弧をdefに揃えることです。
Yes:
def my_method(
self, other_arg: Optional[MyLongType]
) -> Dict[OtherLongType, MyLongType]:
...
pylint では、閉じ括弧を新しい行に移動し、開始括弧に揃えることができますが、これは読みにくくなります。
No:
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:
...
単一の名前とタイプが長すぎる場合は、タイプにエイリアスを使用することを検討してください 。最後の手段は、コロンの後で中断し、4でインデントすることです。
Yes:
def my_function(
long_variable_name:
long_module_name.LongTypeName,
) -> None:
...
No:
def my_function(
long_variable_name: long_module_name.
LongTypeName,
) -> None:
...
3.19.3前方宣言
まだ定義されていない同じモジュールのクラス名を使用する必要がある場合(たとえば、クラス宣言内にクラスが必要な場合、または以下で定義されているクラスを使用する場合)、クラス名に文字列を使用します。
class MyClass:
def __init__(self,
stack: List["MyClass"]) -> None:
3.19.4デフォルト値
PEP-008に従って 、型注釈とデフォルト値の両方を持つ引数に のみ=のまわりにスペースを使用します。
Yes:
def func(a: int = 0) -> int:
...
No:
def func(a:int=0) -> int:
...
3.19.5 NoneType
Python型システムでNoneTypeは、「ファーストクラス」型であり、型付けの目的では、NoneはのNoneTypeエイリアスです。引数がNoneである可能性がある場合は、宣言する必要があります!Unionを使用できますが、他にタイプが1つしかない場合は、Optionalを使用してください。
暗黙的Optionalではなく明示的Optionalを使用します。以前のバージョンのPEP484は、a: Text = Noneを、a: Optional[Text] = Noneとして解釈できましたが、これは推奨される動作ではなくなりました。
Yes:
def func(a: Optional[Text], b: Optional[Text] = None) -> Text:
...
def multiple_nullable_union(a: Union[None, Text, int]) -> Text
...
No:
def nullable_union(a: Union[None, Text]) -> Text:
...
def implicit_optional(a: Text = None) -> Text:
...
3.19.6タイプエイリアス
複合型のエイリアスを宣言できます。エイリアスの名前はCapWordedである必要があります。エイリアスがこのモジュールでのみ使用される場合は、_Privateである必要があります。
たとえば、モジュールの名前とタイプの名前の合計が長すぎる場合:
_ShortName = module_with_long_name.TypeWithLongName
ComplexMap = Mapping[Text, List[Tuple[int, int]]]
他の例は、複雑なネストされた型と関数からの複数の戻り変数(タプルとして)です。
3.19.7タイプを無視する
特別な# type: ignoreコメントがある行の型チェックを無効にすることができます。
pytype は特定のエラーに対する無効化オプションがあります(lintと同様):
# pytype: disable=attribute-error
3.19.8変数の型付け
内部変数の型が推測しにくい、または不可能な場合は、いくつかの方法でその型を指定できます。
行末に# type:コメントを使用
a = SomeUndecoratedFunction() # type: Foo
注釈付きの代入
関数の引数と同様に、変数名と値の間にコロンと型を使用します。
a: Foo = SomeUndecoratedFunction()
3.19.9タプル対リスト
型付きリストには、単一の型のオブジェクトのみを含めることができます。型付きタプルは、単一の繰り返し型、または異なる型の要素のセットのいずれかを持つことができます。後者は通常、関数からの戻り値の型として使用されます。
a = [1, 2, 3] # type: List[int]
b = (1, 2, 3) # type: Tuple[int, ...]
c = (1, "2", 3.5) # type: Tuple[int, Text, float]
3.19.10 TypeVars
Python型システムには ジェネリックがあります。ファクトリ関数TypeVarは、それらを使用する一般的な方法です。
例:
from typing import List, TypeVar
T = TypeVar("T")
...
def next(l: List[T]) -> T:
return l.pop()
TypeVarは制約できます:
AddableType = TypeVar("AddableType", int, float, Text)
def add(a: AddableType, b: AddableType) -> AddableType:
return a + b
typingモジュール内の一般的な事前定義型変数はAnyStrです。すべて同じタイプである必要がある、bytesまたはunicodeに対する複数の注釈に、それを使用します。
from typing import AnyStr
def check_length(x: AnyStr) -> AnyStr:
if len(x) <= 42:
return x
raise ValueError()
3.19.11文字列タイプ
文字列に注釈を付けるための適切なタイプは、コードの対象となるPythonのバージョンによって異なります。
Python 3のみのコードの場合は、strを使用することをお勧めします。Textも許容されます。どちらか一方を一貫して使用してください。
Python 2互換コードの場合は、Textを使用します。まれに、strに意味がある場合があります。通常、戻り値の型が2つのPythonバージョン間で同じでない場合の互換性を支援します。unicode:の使用を避けてください。Python3には存在しません。
この不一致が存在する理由はstrが、Pythonのバージョンによって意味が異なるためです。
No:
def py2_code(x: str) -> unicode:
...
バイナリデータを処理するコードの場合は、bytesを使用します。
def deals_with_binary_data(x: bytes) -> bytes:
...
テキストデータを処理するPython2互換コード(Python 2ではstrまたはunicode、Python 3ではstr)の場合は、Textを使用します。Python 3のみのテキストデータを処理するコードの場合、strを選択します。
from typing import Text
...
def py2_compatible(x: Text) -> Text:
...
def py3_only(x: str) -> str:
...
タイプがバイトまたはテキストのいずれかである場合はUnionを、適切なテキストタイプとともに使用します。
from typing import Text, Union
...
def py2_compatible(x: Union[bytes, Text]) -> Union[bytes, Text]:
...
def py3_only(x: Union[bytes, str]) -> Union[bytes, str]:
...
関数のすべての文字列型が常に同じである場合、たとえば、上記のコードのように、戻り値の型が引数の型と同じである場合は、AnyStrを使用し ます。
このように書くと、コードをPython3に移植するプロセスが簡素化されます。
3.19.12Typingのインポート
typingモジュールからクラスをインポートする場合、常にクラス自体をインポートします。typingモジュールから1行で複数の特定のクラスをインポートすることが明示的に許可されてい ます。例:
from typing import Any, Dict, Optional
typingからインポートするこの方法は、ローカル名前空間にアイテムが追加されることを考えると、typingのすべての名前はキーワードと同様に扱われる必要があり、型付けがされているかどうかに関係なく、あなたのPythonコードで定義されてはいけません。モジュール内のタイプと既存の名前の間に衝突がある場合は、import x as yを使用してインポートします。
from typing import Any as AnyType
3.19.13条件付きインポート
条件付きインポートは、型チェックに必要な追加のインポートを実行時に回避する必要がある例外的な場合にのみ使用してください。このパターンは推奨されません。トップレベルのインポートを許可するようにコードをリファクタリングするなどの代替手段をお勧めします。
型注釈にのみ必要なインポートは、if TYPE_CHECKING:ブロック内に配置できます。
条件付きでインポートされた型は、アノテーション式が実際に評価されるPython 3.6と上位互換性を保つために、文字列として参照される必要があります。
ここでは、型付けのみに使用されるエンティティのみを定義する必要があります。これにはエイリアスが含まれます。そうしないと、モジュールが実行時にインポートされないため、実行時エラーになります。
ブロックは、すべての通常のインポートの直後にある必要があります。
入力インポートリストに空の行があってはなりません。
このリストを通常のインポートリストであるかのように並べ替えます。
import typing
if typing.TYPE_CHECKING:
import sketch
def f(x: "sketch.Sketch"): ...
3.19.14循環依存
型付けによって引き起こされる循環依存は、コードの悪臭です。このようなコードは、リファクタリングに適しています。技術的には循環依存関係を維持することは可能ですが、各モジュールが他のモジュールに依存する必要があるため、ビルドシステムが、それを禁止するでしょう。
循環依存インポートを作成するモジュールをAnyに置き換えます。意味のある名前でエイリアスを設定し、 このモジュールの実際のタイプ名を使用します(Anyの任意の属性はAnyです)。エイリアス定義は、最後のインポートから1行で区切る必要があります。
from typing import Any
some_mod = Any # some_mod.py imports this module.
...
def my_method(self, var: "some_mod.SomeType") -> None:
...
3.19.15ジェネリック
注釈を付けるときは、ジェネリック型の型パラメーターを指定することをお勧めします。それ以外の場合、 ジェネリックのパラメータはAnyであると見なされます。
def get_names(employee_ids: List[int]) -> Dict[int, Any]:
...
# These are both interpreted as get_names(employee_ids: List[Any]) -> Dict[Any, Any]
def get_names(employee_ids: list) -> Dict:
...
def get_names(employee_ids: List) -> Dict:
...
ジェネリックに最適な型パラメーターがAnyである場合は、それを明示的にしますが、多くの場合TypeVarが、より適切である可能性があることに注意してください。
def get_names(employee_ids: List[Any]) -> Dict[Any, Text]:
"""Returns a mapping from employee ID to employee name for given IDs."""
T = TypeVar('T')
def get_names(employee_ids: List[T]) -> Dict[T, Text]:
"""Returns a mapping from employee ID to employee name for given IDs."""
一貫性を保ちなさい。
コードを編集している場合は、数分かけて周囲のコードを確認し、そのスタイルを判断してください。それらが、すべての算術演算子の前後にスペースを使用する場合は、あなたもそうすべきです。それらのコメントの周りにハッシュマークの小さなボックスがある場合は、あなたのコメントにもハッシュマークの小さなボックスを配置します。
スタイルガイドラインを持つことのポイントは、人々があなたがそれをどのように言っているかではなく、あなたが言っていることに集中できるように、コーディングの共通の語彙を持つことです。ここではグローバルスタイルのルールを提示して、人々が語彙を理解できるようにしますが、ローカルスタイルも重要です。あなたがファイルに追加するコードがそのファイルの周りの既存のコードと大幅に異なって見える場合、読者がファイルを読みに行くときにリズムが狂ってしまいます。これは避けてください。
以上