すべてのベテラン Python開発者 (Pythonistas)Pythonicコードの記述について説教する。 あなたがPythonicコードを書くことに時間を費やした人なら、あなたはベストプラクティスに出くわすでしょう。 しかし、Pythonicコードとは正確に何であり、主要な問題点をどのように思い出すか/明白な(悪い)プラクティスを回避する必要がありますか?
さいわい、Pythonコミュニティは、比較的シンプルで完全なコードスタイルガイドラインと「Pythonic」イディオムに恵まれています。 これらは、Pythonicコードの可読性が高い主な理由のXNUMXつです。 Pythonの中核は可読性と単純化した構文です。
この投稿では、いくつかの非常に重要なスタイルガイドラインとPythonicイディオム、およびレガシーコードの処理方法について説明します。
XNUMX行にXNUMXつのコードステートメント
ばらばらのステートメントをXNUMX行で記述している場合は、Pythonの本質に違反しています。 唯一の例外は、リスト内包表記と他のいくつかの複合ステートメントです。 これらは、その簡潔さと表現力のために許可され、受け入れられます。
悪い習慣
print 'foo'; print 'bar' if x == 1: print 'foo' if <complex comparison> and <other complex comparison>: # do something
ベストプラクティス
print 'foo'
print 'bar' if x == 1: print 'foo' cond1 = <complex comparison>
cond2 = <other complex comparison>
if cond1 and cond2: # do something
明示的なコード
コードを記述する最も単純な(そして最も理解しやすい)方法は常に最良です。
悪い習慣
def make_complex(*args): x, y = args return dict(**locals())
上記のコードは次を返します:{'args': (1, 2), 'x': 1, 'y': 2}
xとyだけが必要なため、これは冗長ですが、 'args'も返します。
また、次の場合:
def make_complex(*args): x,y = args z = x+y return dict(**locals())
上記の関数は、ローカル辞書でも 'z':3を返します。 ベストプラクティスは、必要なものを指定し、最も簡単な方法に従うことです。 物事をシンプルかつ明確にすることを忘れないでください。
この悪い習慣のもう2つの落とし穴は、関数を呼び出すときにXNUMXつ以上のパラメーターを渡す場合です。 make_complex(1,2,3)
、それはスローされます valueError
このような:
ベストプラクティス
def make_complex(x, y): return {'x': x, 'y': y}
関数に引数を渡す
関数に引数を渡す方法はXNUMXつあります。
- 位置引数: これらは最も単純な形式の引数です。 位置引数は完全に関数の意味の一部であり、それらの順序は定義された順序になります。 たとえば、
cal_area(length, breadth)
orsend_msg(message, recipient)
、開発者はこれらの2つの関数にはXNUMXつの引数またはその順序が必要であることを覚えておく必要はありません。
Note:上記のXNUMXつの例では、次のようなキーワードを使用して、異なる順序で関数を呼び出すこともできます。 cal_area(breadth = 40.0, length=90)
or send_msg(recipient='Mak', message='Hello there!')
.
-
キーワード引数: またとして知られています
kwargs
、これらは関数に渡されるオプションのパラメーターとしてよく使用されます。 関数にXNUMXつまたはXNUMXつ以上の定位置パラメーターがある場合、そのシグニチャーは覚えにくくなります。
クワーグはデフォルト値で便利です。 たとえば、より良い書き方send_msg
関数は次のようになります:send_message(message, recipient, cc=None, bcc=None)
。 ここに、cc
およびbcc
オプションであり、値が渡されない場合は「なし」として返されます。 -
任意の引数リスト: 関数のビジネスロジックが拡張可能な数の位置引数を必要とする場合は、
*args
構成。 関数内では、args
残りのすべての位置引数のタプルになります。 例えば、send_msg(message, *args)
各受信者を引数として呼び出すことができます:send_msg('Hello there!', 'God', 'Mom', 'Cthulhu')
、そして関数スコープはargs
に等しい('God', 'Mom', 'Cthulhu')
. -
任意のキーワード引数辞書:関数が不定の一連の名前付き引数を必要とする場合は、
**kwargs
構築します。 関数本体では、kwargsは、関数シグニチャーの他のキーワード引数によってキャッチされていない、渡されたすべての名前付き引数のディクショナリーになります。
使い方*args
、Pythonは関数に可変長の非キーワード引数を渡します—しかし、キーワード引数を渡したい場合はどうでしょうか? 使用する**kwargs
、キーワード引数の可変長を関数に渡すことができます。
たとえば、次の関数の場合:
def introduction(**data): print("nData type of argument:",type(data)) for key, value in data.items(): print("{} is {}".format(key,value))
introduction(Firstname="Sita", Lastname="Sharma", Age=22, Phone=1234567890)
introduction(Firstname="John", Lastname="Wood", Email="johnwood@nomail.com", Country="Wakanda", Age=25, Phone=9876543210)
出力は次のようになります。
Data type of argument: <class 'dict'>
Firstname is Sita
Lastname is Sharma
Age is 22
Phone is 1234567890 Data type of argument: <class 'dict'>
Firstname is John
Lastname is Wood
Email is johnwood@nomail.com
Country is Wakanda
Age is 25
Phone is 9876543210
Note:任意の引数リストと同じ注意が必要です。 理由も同様です。これらの強力な手法は、必要性が証明されている場合にのみ使用するものであり、関数の意図を表現するのに単純で明確な構成で十分な場合は使用しないでください。
コーディングスタイルガイドに従っている場合、Python関数は次のようになります。
- 読みやすい(名前と引数に説明は必要ありません)
- 変更が簡単(新しいキーワード引数を追加しても、コードの他の部分が壊れることはありません)
返品明細書
関数が複雑になるにつれて、関数の本体内に複数のreturnステートメントが含まれる可能性があります。 ただし、明確な意図と読みやすさのレベルを維持するために、関数本体の複数の出力ポイントで意味のある値を返さないようにすることをお勧めします。
たとえば、複数の出力ポイントを追加せずに例外を発生させる方法については、以下の例(インラインコメントで説明)をご覧ください。
悪い習慣
def complex_function(a, b, c): if not a: return None if not b: return None # Some complex code trying to compute x from a, b and c if x: return x if not x: # Some Plan-B computation of x return x
ベストプラクティス
def complex_function(a, b, c): if not a or not b or not c: raise ValueError("The args can't be None") # Raising an exception is better # Some complex code trying to compute x from a, b and c # Resist temptation to return x if succeeded if not x: # Some Plan-B computation of x return x # One single exit point for the returned value x will help when maintaining the code.
慣用的なPythonを書く
イディオムは、文字通り意味をなさないフレーズですが、それが発生した文化に精通していれば意味があります。 プログラミングイディオムも同じです。 それらは、特定のプログラミング言語またはパラダイムで日常的に行う小さなことであり、その文化に精通している人にのみ意味があります。
Pythonの初心者は慣用的なPythonの記述に気付かない可能性があるため、いくつかの一般的なPythonの慣用句をリストしました。
開梱
リストを展開するときにリストの要素に名前または参照を割り当てる場合は、 enumerate()
:
for index, item in enumerate(some_list): # do something with index and item
スワップ変数を使用できます。
a, b = b, a
ネストされた解凍も機能します:
a, (b, c) = 1, (2, 3)
Python 3では、 PEP3132 拡張アンパックの新しい方法を導入しました:
a, *rest = [1, 2, 3]
# a = 1, rest = [2, 3]
a, *middle, c = [1, 2, 3, 4]
# a = 1, middle = [2, 3], c = 4
使い捨て変数の作成
何かを割り当てる必要がある場合(たとえば、アンパック時)、その変数は必要ない場合は、 __
:
filename = 'foobar.txt'
basename, __, ext = filename.rpartition('.')
Note:
多くのPythonスタイルガイドでは、単一の下線の使用を推奨しています _
二重下線ではなく使い捨て変数の場合 __
ここをお勧めします。 問題は _
のエイリアスとして一般的に使用されます gettext()
関数、および対話式プロンプトで最後の操作の値を保持するためにも使用されます。
代わりにXNUMXつの下線を使用することも、同じくらい簡単でほとんど同じです。 この方法の利点は、これらの他のユースケースのいずれかに誤って干渉するリスクを排除できることです。
同じものの長さNのリストを作成する
Pythonリストを使用する *
単純なリストとネストされたリストも作成する演算子:
nones = [None]*4
foures_of_fours = [[4]]*5
出力:
[なし、なし、なし、なし]
[[4]、[4]、[4]、[4]、[4]]
コレクション内のアイテムを検索する
コレクションを検索する必要がある場合があります。 リストとセットのXNUMXつのオプションを見てみましょう。 たとえば、次のコードを見てください。
def in_test(iterable): for i in range(1000): if i in iterable: pass
from timeit import timeit
timeit( "in_test(iterable)", setup="from __main__ import in_test; iterable = set(range(1000))", number=10000) Output: 0.5591847896575928 timeit( "in_test(iterable)", setup="from __main__ import in_test; iterable = list(range(1000))", number=10000) Output: 50.18339991569519 timeit( "in_test(iterable)", setup="from __main__ import in_test; iterable = tuple(range(1000))", number=10000) Output: 51.597304821014404
両方の機能は同じに見えます。 lookup_set()
Pythonのセットはハッシュテーブルであるという事実を利用しています。 ただし、XNUMXつのルックアップパフォーマンスは異なります。つまり、セットはO(log n)を使用しますが、リストはO(n)の時間の複雑さを持っています。
アイテムがリストにあるかどうかを判断するには、一致するアイテムが見つかるまで、Pythonが各アイテムを調べる必要があります。 これは、特に長いリストの場合、時間がかかります。 一方、セットでは、アイテムのハッシュによって、一致するアイテムを探すセット内の場所がPythonに通知されます。 その結果、セットが大きい場合でも、検索を迅速に行うことができます。
これらのパフォーマンスの違いのため、以下の場合はリストではなくセットまたは辞書を使用することをお勧めします。
- コレクションには多数のアイテムが含まれます
- コレクション内のアイテムを繰り返し検索します
- 重複するアイテムはありません
辞書要素にアクセスする
dict.has_key()メソッドは使用しないでください。 代わりに、xを使用してください in
d構文、またはデフォルトの引数をdict.get()に渡します。 Python 3.xで削除.
Note:Python2は2020年に廃止される予定です。ほとんどのPythonパッケージはPython 3.xのアップデートのリリースを停止しているため、あらゆる種類の開発にはPython 2.xを使用することをお勧めします。 続きを読む こちら.
悪い習慣
d = {'foo': 'bar'}
if d.has_key('foo'): print d['foo'] # prints 'bar'
else: print 'default_value'
ベストプラクティス
d = {'foo': 'bar'} print d.get('foo', 'default_value') # prints 'bar'
print d.get('thingy', 'default_value') # prints 'default_value' # alternative
if 'hello' in d: print d['foo']
リストのフィルタリング
繰り返し処理中は、リストからアイテムを削除しないでください。 どうして? 複数の参照を介してリストにアクセスする場合、参照のXNUMXつを再配置するだけで(リストオブジェクト自体を変更しない)場合、微妙で壊滅的なバグが発生する可能性があります。 それについてもっと読む こちら.
悪い習慣
# Filter elements greater than 4
num_list = [1, 2, 3]
for i in num_list: if i > 2: num_list.remove(i)
リストを複数回パスしないでください。
while i in num_list: num_list.remove(i)
ベストプラクティス
リスト内包表記またはジェネレータ式を使用します。
# comprehensions create a new list object
filtered_values = [value for value in sequence if value != x] # generators don't create another list
filtered_values = (value for value in sequence if value != x)
リストの値を更新する
割り当てによって新しいオブジェクトが作成されることはありません。 XNUMXつ以上の変数が同じリストを参照している場合、そのうちのXNUMXつを変更するとすべてが変更されます。
悪い習慣
# Add three to all list members.
a = [3, 4, 5]
b = a # a and b refer to the same list object for i in range(len(a)): a[i] += 3 # b[i] also changes
ベストプラクティス
a = [3, 4, 5]
b = a # assign the variable "a" to a new list without changing "b"
a = [i + 3 for i in a]
b = a[:] # even better way to copy a list
with open
ファイルから読み取る構文。 これにより、ファイルが自動的に閉じます。
悪い習慣
f = open('file.txt')
a = f.read()
print a
f.close()
ベストプラクティス
with open('file.txt') as f: for line in f: print line
with
メソッドは、ブロック内で例外が発生した場合でも、常にファイルを閉じることができるため、優れています。
レガシーコードの扱い
Pythonで優れたコードを書くための基本をカバーしました。 Pythonで大きなプロジェクトを処理する技術を見る価値があります。 どのようにして新しいオープンソースまたはクローズドソースのプロジェクトを取り上げることができますか? レガシーコードをリファクタリングする手順は何ですか? 新しいプロジェクトのスピードを上げるためのベストプラクティスは何ですか?
多くの場合、新しい組織に参加すると、理解してリファクタリングするためのコードベースが与えられます。あるいは、リファクタリングするためにレガシーコードを取り上げる必要があります。 時々、この状況のおかげで、あなたは自分自身に深い苦痛を感じ、出発点を理解することができなくなります。
この時点で、「レガシーコード/プロジェクト」を定義して、全員が同じページにいるようにすることが重要です。 これがあなたが出くわすものです:
- 永遠に存在していた「古い」プロジェクト
- いかなる種類のテストもないコードベース
- 誰も取り組みたくないプロジェクト
- 「これに取り組んだ全員が数年前に会社を辞めました…」
上記はすべてある程度正しいですが、改善の余地がたくさんあることに誰もが気付かないうちに、プロジェクトが急いで行われて本番環境に入れられる場合があります。 それでは、レガシープロジェクトにどのように取り組みますか?
以下は、リファクタリングをより簡単かつスムーズにするために実行する必要がある手順の簡単なリストです。
- まず第一に、プロジェクトがバージョン管理システムにあることを確認してください。
- コメントアウトされたコードを削除します。 (プロジェクトが本稼働になったら、必ずコメント付きのコードを削除してください。)
- テストの実行/テストの追加。 テストカバレッジが80%以上であることを確認してください。 使用する
pytest
またはテストカバレッジを追跡するための同様のPythonパッケージ。 - ピリント/ハゲタカ。 常に何らかのタイプの実行を検討する リンター コードを調べて、それがいかに「健康」かを確認します。 探してみてください:
- 未使用の変数
- 潜在的なバグとして指摘されているもの
- Flake8やPEP8などのフォーマッターを使用します。 これらのガイドラインを使用して、Pythonコードを再フォーマットし、より多くのコードにすることができます。 PEP8 苦情。
- (上記のように)より慣用的なPythonを記述します。
まとめ
爆発的なPythonコミュニティと新進のPythonistasにより、データサイエンス、Web開発、モバイル開発、AIなどのほぼすべての開発分野でPythonを使用しています。そのため、常にエンタープライズクラスのコードを出荷することの重要性が増しています。適切なガイドラインに従ってください。
これらの基本的なツールのおかげで、そしてPython言語自体の美しさのおかげで、素晴らしいコードと製品を生成することは、恐ろしい命題である必要はありません。 これらのガイドラインを完了したので、先に進んでこれらを試してください。 オープンソースのPythonプロジェクト!
その他のPythonのベストプラクティスについては、次の投稿をご覧ください。