The Dabsong Conshirtoe

技術系の話を主にします。

PythonのGood Parts

pythonを使い始めて1年半ほどになります。

ここらで、pythonの良いと感じたとこをまとめてみよう。

1. リスト内包表記

リスト内包表記とは、以下のようなものです。

# 通常のリスト生成
r = []
for i in range(100):
    r.append(i)

# リスト内包表記
r = [i for i in range(100)]

良いと思うとこを箇条書き。

  • 簡潔で直感的。
  • 素のforループと違い、ループ処理がインタープリターにより最適化されるのでループ速度も向上します。(「Learning Python」では、"often roughly twice as fast"とあります。)
  • 抽出条件や抽出した要素に対する処理を記述できる。

例えば、

# 対象のリストから3以上の値を取り出して2倍して新たなリストを作る
r = [i * 2 for i in target if i >= 3]

# これを素のfor文で書くとコードブロックが2段階発生して雑多な感じになります。
r = []
for i in target:
    if i < 3:
        continue
    r.append(i * 2)

なお、pythonにはリストの各要素に関数を適用するmap関数や、ある条件の要素抽出するfilter関数もあります。

# 全ての文字にord関数を適用
r = map(ord, 'spam')
# リスト内包の場合
r = [ord(c) for c in 'spam']

# 3で割り切れる値を取り出す
r = filter(lambda x: x % 3 == 0, range(10)) 
# リスト内包の場合
r = [i for i in range(10) if i % 3 == 0]
  • ネストしたfor文も可能。(ただしやりすぎると可読性を損なう。)
# ネストしたfor文を内包表記
nested = [x + y for x in 'spam' for y in 'SPAM']

# 素で書くとこう。
nested = []
for x in 'spam':
    for y in 'SPAM':
        nested.append(x + y)

リストではなくジェネレータが欲しい時は()で囲みます。(リスト内包表記とは言わないけど)

g = (i ** i for i in range(10))

2. キーワード引数

元々Java使いだったのですが、これはかなり感動しました。
キーワード引数とは、関数に渡す引数を "keyword = value" の形で渡せる機構です。

def f(x, y, z):
  ...
f(x=2, y=4, z=5)
# 順序を崩すことも可能
f(y=2, z=3, x=5)
# dictを展開して渡すことも可能
args = {"x":2, "y": 3, "z": 4}
f(**args)

これ、何が良いってコードの可読性が増す点だと思います。
上記の関数のように引数が3個以上ある関数だと、どの位置がどの引数と対応づけられているか確認するのがなかなか手間だったりしますし、うっかり位置を間違えてしまうこともあります。
キーワード引数の仕組みを使えば、順序を気にすることもないですし初めてコードを読む人にとっても何を渡しているかわかりやすいコードになります。

デフォルト引数

またキーワード引数では以下のように、引数のデフォルト値を設定できます。

def load_users_by_type(user_type, max_results=100):
  ユーザーをuser_typeで絞ってid順にDBから取得する処理

# 呼び出し側は、max_resultsなしで呼べる。
users = load_users_by_type(1)

# max_resultsを変動させたい場合
users = load_users_by_type(1, max_results=200)

max_resultsが大抵の場合は100だが、特定のケースでは200取りたい、というような場合に便利です。

それ以上に便利なシチュエーションが、メソッド引数の変更時。

例えば、load_users_by_typeメソッドにソートキーを指定したくなったとします。

キーワード引数が無い言語ならば、load_users_by_typeに引数を追加すると呼び出し箇所全てに修正が入ることになります。
それを避けるためにsort_key引数を加えたラッパーメソッドを作るというのもイマイチな気がします。

しかし、キーワード引数があるとload_users_by_typeの引数を、他の呼び出し箇所を気にせず変えられます。

def load_users_by_type(user_type, max_results=100, sort_key='id'):

まぁメソッド引数がそう簡単に変更されないような設計をするのが望ましいのですが、そううまくはいかないので、キーワード引数は非常に助かります。
キーワード引数はRuby2.0でもサポートされるみたいですね(デフォルト値の仕組みはすでにありますが)。

Learning Python

Learning Python

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック (Theory in practice)

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック (Theory in practice)