Pythonのlambda(ラムダ式、無名関数)の使い方
Pythonではdef文で関数を定義するが、lambda(ラムダ式)で名前を持たない無名関数を作成することもできる。
lambdaは引数として関数(呼び出し可能なオブジェクト)を指定する場合などに使うと便利。書き方および使い方を説明する。
- def文とlambda式の対応関係
- lambda式でif文を使う
- PEP8ではlambda式には名前を付けないのが推奨
- lambda式の具体的な使い方・活用例
sorted()
,sort()
,max()
,min()
の引数key
にlambda式map()
やfilter()
の第一引数にlambda式
def文による関数の定義については以下の記事を参照。
def文とlambda式の対応関係
def文による関数定義とそれに相当するラムダ式での無名関数の対応関係は以下のようになる。便宜上、ラムダ式に名前を割り当てている(ラムダ式を変数に代入している)が、Pythonのコーディング規約であるPEP8では非推奨となっている。後述。
def 名前(引数, 引数, ...):
return 式
名前 = lambda 引数, 引数, ...: 式
具体的には以下のようになる。この例のようにデフォルト引数を指定することもできる。
def add_def(a, b=1):
return a + b
add_lambda = lambda a, b=1: a + b
print(add_def(3, 4))
# 7
print(add_def(3))
# 4
print(add_lambda(3, 4))
# 7
print(add_lambda(3))
# 4
lambda式でif文を使う
ラムダ式では複数行にまたがる文を使うことはできないが、if文に相当する三項演算子は使用可能。
get_odd_even = lambda x: 'even' if x % 2 == 0 else 'odd'
print(get_odd_even(3))
# odd
print(get_odd_even(4))
# even
三項演算子の書き方などの詳細は以下の記事を参照。
PEP8ではlambda式には名前を付けないのが推奨
これまでの例のようにラムダ式に名前を割り当てる(ラムダ式を変数に代入する)とPythonのコーディング規約PEP8の自動チェックツールなどでWarningが出ることがある。
Do not assign a lambda expression, use a def (E731)
ラムダ式は呼び出し可能なオブジェクトを引数で渡すときなどに名前を付けずに使うためのもので、名前を付けて関数を定義する場合はdef
を使うべき、というのがPEP8の考え方。
あくまでもコーディング規約PEP8の推奨で、Pythonの文法的にはエラーではなく実行可能。
lambda式の具体的な使い方・活用例
sorted(), sort(), max(), min()の引数keyにlambda式
リストをソートする組み込み関数sorted()
やリストのメソッドsort()
、最大値や最小値を返す組み込み関数max()
やmin()
には引数key
がある。
key
には、ソートや最大値・最小値の算出の前に(各要素が比較される前に)リストの各要素に適用される関数を指定する。
組み込み関数sorted()
を例に説明する。
文字列のリストは、デフォルトではアルファベット順にソートされる。
l = ['Charle', 'Bob', 'Alice']
l_sorted = sorted(l)
print(l_sorted)
# ['Alice', 'Bob', 'Charle']
文字数を返す組み込み関数len()
を引数key
に指定すると、文字数が少ない順にソートされる。
print(len('Alice'))
# 5
l_sorted_len = sorted(l, key=len)
print(l_sorted_len)
# ['Bob', 'Alice', 'Charle']
ここでラムダ式を使うと、任意の関数を各要素に適用してその結果を元にソートできる。例えば2文字目を取得するラムダ式を引数key
に指定すると、2文字目のアルファベット順にソートされる。
print((lambda x: x[1])('Alice'))
# l
l_sorted_second = sorted(l, key=lambda x: x[1])
print(l_sorted_second)
# ['Charle', 'Alice', 'Bob']
もちろんdef文で関数を定義してkey
に指定してもいいが、そのあと同じ関数を繰り返し使うのでなければラムダ式で書いたほうが無駄がない。
上述のようにラムダ式では複数行の処理を定義できないので、複雑な処理を行う場合はdef文で関数を定義する。組み込み関数len()
の例と同様に、例えばfunc()
という名前の関数を定義した場合はkey=func
とすればよい。
map()やfilter()の第一引数にlambda式
リストの各要素に対して関数を適用する組み込み関数map()
や、条件を満たす要素のみ抽出する組み込み関数filter()
では、第一引数に関数、第二引数にリストなどのイテラブルオブジェクトを指定する。
任意の関数を指定したい場合、def文で関数を定義するよりラムダ式で無名関数を指定したほうが簡潔に書ける。
なお、map()
関数やfilter()
関数と同様の処理はリスト内包表記やジェネレータ式(ジェネレータ内包表記)でも書ける。これについても例を示す。
書籍『Effective Python』ではmap()
関数やfilter()
関数よりもリスト内包表記やジェネレータ式(ジェネレータ内包表記)を使うのが推奨されている。
map()関数の例
map()
関数の例として、第一引数に値を二乗するラムダ式を指定する。
l = [0, 1, 2, 3]
map_square = map(lambda x: x**2, l)
print(map_square)
# <map object at 0x1072fd128>
print(list(map_square))
# [0, 1, 4, 9]
Python3からmap()
はリストではなくイテレータを返すようになったので注意。
リストを取得したい場合はリスト内包表記でも書ける。ラムダ式を書くより簡潔。
- 関連記事: Pythonリスト内包表記の使い方
l_square = [x**2 for x in l]
print(l_square)
# [0, 1, 4, 9]
イテレータを取得したい場合はジェネレータ式(ジェネレータ内包表記)を使う。
g_square = (x**2 for x in l)
print(g_square)
# <generator object <genexpr> at 0x1072b6d00>
print(list(g_square))
# [0, 1, 4, 9]
filter()関数の例
filter()
関数の例として、第一引数に偶数を真True
とするラムダ式を指定する。
filter()
もmap()
と同様にPython3からはイテレータを返すようになっている。
filter_even = filter(lambda x: x % 2 == 0, l)
print(list(filter_even))
# [0, 2]
これもリスト内包表記やジェネレータ式(ジェネレータ内包表記)でも書ける。
l_even = [x for x in l if x % 2 == 0]
print(l_even)
# [0, 2]
リストの要素の抽出などについて、より詳しくは以下の記事を参照。