[Python入門]ローカル関数とラムダ式Python入門(3/3 ページ)

» 2023年10月16日 05時00分 公開
[かわさきしんじDeep Insider編集部]
前のページへ 1|2|3       

ラムダ式

 最後に「無名関数」(anonymous function)を作成するラムダ式について簡単に取り上げておこう。「無名関数」とはその名の通り、「名前を持たない関数」のこと。1行程度で記述できるちょっとした関数を、手っ取り早く定義したいときにラムダ式を便利に使える。

 「ラムダ式」とは、「lambda」で始まり、その後にその無名関数のパラメーターと、その関数で行う計算式を記述する。

lambda パラメータ:

lambda式の構文

 「パラメーター」にはこの関数のパラメーターを記述する。受け取らないときには何も書かずにコロン「:」を書く。パラメーターが1つなら、パラメーターを書いて次にコロンを書く。パラメーターが複数あるときには、それらをカンマで区切って並べて最後にコロンを書く。コロンに続けて、「式」に何らかの式を1つだけ記述する。その計算結果が関数の戻り値となる。簡単な例を以下に示す。

hello = lambda x: 'Hello ' + x
hello('world')

ラムダ式を使った関数の定義

 このlambda式では「パラメーター」に「x」を指定して、「式」の部分には「'Hello ' + x」という文字列の結合を行う式が書かれている。よって、これは「文字列を渡すと、'Hello 〜'という文字列を返す無名関数」を作成する。

 実行結果を以下に示す。

実行結果 実行結果

 もちろん、無名関数といっても、それを明示的に呼び出すには上のコードのように変数に代入して、その変数を使って呼び出す必要がある。ただし、ラムダ式を変数に代入するのは推奨されていない。このようなときには素直にdef文で関数を定義することが推奨されている。

 ではどんなところでラムダ式が使えるかというと、先ほど見たapply関数のように関数に引数として関数を渡すような場面だ。

def apply(func, param):
    return func(param)

関数とそれに渡す引数を受け取り、関数を呼び出すapply関数(再掲)

 このように関数を受け取る関数に、簡単な関数を渡すのに、いちいちそれを定義するのがまだるっこしいというときもある。そんなときには、以下のようにラムダ式を直接引数として渡せる。

apply(lambda x: 'Hello ' + x, 'Python')

関数の引数にラムダ式を記述する

 これはあまり実用的な例ではないので、もう1つ例を示そう。Pythonには組み込みのfilter関数がある。

filter関数

filter(func, iterable)


 iterableに渡した反復可能オブジェクトの各要素について、funcに渡した関数でそれを評価した結果が真となるものだけを要素とする新しいイテレータ(データの流れを表現するオブジェクト。後続の回で説明予定)を返す。

パラメーター 説明
func iterableの各要素を引数に取り、その値を基に真か偽かを判定する関数
iterable 元の要素を格納する反復可能オブジェクト
filter関数のパラメーター


 例えば、文字列'0123456789'から偶数の文字だけを抜き出してみよう。ある文字が偶数かどうかを調べるには、それをint関数で整数化して、2で割った余りが0かどうかを調べればよい。つまり、次のような関数があればよい。

def is_even(x):
    return int(x) % 2 == 0

result = filter(is_even, '0123456789')
for number in result:
    print(number)

ある文字が偶数の文字かどうかを調べる関数「is_even」と、それを使って偶数の文字だけをフィルタリングして、それらを表示するコード

 これを実行すると、次のように0、2、4、6、8だけが表示される。

フィルタリングによって、偶数の文字だけが得られた フィルタリングによって、偶数の文字だけが得られた

 上のコードをラムダ式を使って書き直すと次のようになる。

result = filter(lambda x: int(x) % 2 == 0, '0123456789')
for number in result:
    print(number)

ラムダ式を使って書き直したコード

 def文を使って関数を定義するよりも、その内容を式として直接記述できるので、簡潔なコードになる。ただし、これはfilter関数を使わずに「リスト内包表記」と呼ばれる記法を使うと次のようにも書ける。リスト内包表記については、次回に取り上げよう。

result = [x for x in '0123456789' if int(x) % 2 == 0]
for number in result:
    print(number)

リスト内包表記を使って書き直したコード

 多くの場合、単純な判断を行うためだけに関数を定義して、それを他の関数に渡すのは煩わしい作業となる。現在書いているコード位置からカーソルを別の場所に移動して、関数を定義し、元の場所に戻って「さてどんなコードを書くんだっけ?」となることもあるかもしれない。そうではなく、先ほどの例であれば「filter関数には偶数かどうかを調べるラムダ式を直接記述しよう」となると、頭の中で考えていることをダイレクトにプログラムコード化できて、プログラマーの思考を邪魔することが少なくなる。最初は難しいかもしれないが、少しずつ慣れていけば、ラムダ式を便利に使えるようになるはずだ。

まとめ

 今回はPythonの関数が「第一級オブジェクト」として、変数に代入したり、関数の引数や戻り値にしたりできる特性と、そのときにどんなことが起きているかを見た。そして、最後に無名関数を簡潔に記述するための「ラムダ式」を紹介した。次回からはリストやタプルなどのデータ構造について見ていく。

今回のまとめ:オブジェクトとしての関数

  • 関数は、数値や文字列と同じように、変数に代入できる
  • 変数に関数を代入すると、その変数にかっこと引数を付加することで元の関数を呼び出せる
  • 関数をパラメーターに受け取る関数もある
  • 関数を戻り値とする関数もある
  • これらの特性からPythonでは「関数は第一級オブジェクトである」ということがある
  • 関数内で定義する関数を「ローカル関数」と呼ぶ
  • ローカル関数を含む関数を「エンクロージャー」と呼ぶ
  • ローカル関数の中で、エンクロージャーのローカル変数を利用するものを「クロージャ」と呼ぶ
  • エンクロージャーのローカル変数をローカル関数が利用するコードでは、エンクロージャーが呼び出されてローカル関数が定義された時点で、そのローカル変数の値が「キャプチャー」される
  • キャプチャーされた変数は、ローカル関数が存在する限りは存在する
  • ローカル関数内では、そのローカルスコープ→エンクロージャーのローカルスコープ→グローバルスコープ→ビルトインスコープの順に名前解決が行われる
  • ローカル関数の中でエンクロージャーで定義したローカル変数の値を変更するにはnonlocal文を使ってノンローカル宣言を行う
  • ラムダ式は「単一の式」だけで構成される「無名関数」を簡潔な記述で定義できる
  • ラムダ式は関数を利用できる場所に記述できる

「Python入門」のインデックス

Python入門

前のページへ 1|2|3       

Copyright© Digital Advantage Corp. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。