nonlocal文とは?
Pythonでは、関数の中に関数を定義する「入れ子(ネスト)関数」を作れます。
通常、内側の関数から外側の関数で定義した変数を読み取ることはできますが、代入することはできません。もし内側の関数で同じ名前の変数に値を代入しようとすると、Pythonは「内側の関数で新しいローカル変数を定義した」と解釈してしまい、外側の関数の変数は変更されないままです。
nonlocal文は、内側の関数で扱う変数が「今いるローカルの変数ではなく、ひとつ外側の関数の変数である」ことを明示的に宣言するためのキーワードです。
nonlocal文の特徴
- ネスト関数から、ひとつ外側の関数の変数を書き換えられるようにする
- global文とは異なり、一番外側ではなく、あくまで外側の関数の変数を対象とする
- 意図しないローカル変数の作成を防ぎ、コードの動作を明確にする
- クロージャ(関数閉包)やデコレーターといった高度な機能の実装に役立つ
- Python 3から導入された機能である
【関連】
Pythonをもっと詳しく学ぶならpaizaラーニング
基本構文
nonlocalキーワードの基本的な使い方を見ていきましょう。構文はシンプルで、内側の関数(ネスト関数)の中で「nonlocal 変数名」のように宣言するだけです。
まず、nonlocalを使わない場合に何が起こるかを確認してみましょう。
出力結果
内側の関数: ネコ
外側の関数: イヌこの例では、inner_functionでanimal = "ネコ"と代入しても、外側のanimal("イヌ")は変わりません。inner_functionの中でanimalという名前の新しいローカル変数が作られた、とPythonが判断するためです。結果として、inner_functionの中(ネコ)と外(イヌ)で、変数の値が異なっています。
次に、nonlocalを使ったケースを見てみましょう。
出力結果
内側の関数: ネコ
外側の関数: ネコこの例では、nonlocal animalと宣言したため、inner_function内のanimalは「外側の関数のanimal」を指すようになります。
animal = "ネコ"という代入が外側の変数を直接書き換えるため、outer_functionの最後のprint文でも"ネコ"が出力されました。nonlocalは状態を保持したい場合や、関数間でデータを共有したい場合に特に役立ちます。
実用例
nonlocal文の実際の実用例を詳しく見ていきましょう。nonlocalは、クロージャ(関数閉包)の実装、カウンターの作成、状態を保持する関数など、さまざまな場面で活躍します。
カウンター関数の実装
nonlocalを使うと、関数を呼び出すたびに値をカウントアップ(1ずつ増やす)するような関数を簡単に作れます。
出力結果
1
2
3この例では、create_counter関数が内側のcounter関数を返しています。nonlocal count宣言により、counter関数が呼び出されるたびに外側のcount変数が更新され、その状態が保持されます。そのため、my_counter()を実行するたびに、countが1ずつ増えていきます。
動物の成長シミュレーション
動物の成長過程をシミュレーションする関数を作ってみましょう。nonlocalを使って年齢を管理します。
出力結果
ポチは1歳になりました
ポチは2歳になりました
ポチは3歳になりましたこの例では、create_petが返すgrow関数を実行するたびに、nonlocal ageによって外側のage変数が1ずつ加算されます。create_petが呼び出されたときに作られたage(初期値0)が、my_dog()の呼び出しのたびに更新され、成長が表現されています。
状態を持つ計算機の作成
nonlocalを利用して、簡単な計算履歴(ログ)を保持する計算機を作ってみましょう。
出力結果
['5 + 3 = 8', '2 + 7 = 9']この例では、add関数が呼び出されるたびに、nonlocal historyによって外側のhistoryリストに計算結果の文字列が追加されます。add関数とget_history関数は、create_calculatorの実行時に定義されたhistoryリストを共有しています。
動物園の管理システム
動物園の動物を管理するシステムを作ってみましょう。
出力結果
キリンを追加しました
ゾウを追加しました
['キリン', 'ゾウ']この例では、add_animal関数がnonlocal animals宣言により、外側のanimalsリストを参照しています。.append(animal)でリストに動物が追加されると、list_animals関数も同じリストを参照しているため、追加された動物を含む最新のリストが返されます。
言葉を覚えるオウム関数
言葉を覚えて繰り返すオウム関数を作ってみましょう。
出力結果
オウムは「こんにちは」を覚えました
オウムは「さようなら」を覚えました
オウムは覚えた言葉を話します: こんにちは, さようならこの例では、learn関数がnonlocal words宣言によって、外側のwordsリストに新しい単語を追加していきます。speak関数も同じwordsリストを参照しているため、learnで覚えたすべての単語をカンマ(,)でつないで出力できています。
温度変換ツール
温度の単位変換(摂氏から華氏)を行い、最後の変換結果を記録するツールを作成してみましょう。
出力結果
30℃ → 86.0℉この例では、convert関数(摂氏を華氏に変換)が呼び出されるたびに、nonlocal last_conversionによって外側のlast_conversion変数が上書きされます。temp["convert"](25)が実行された後、temp["convert"](30)が実行されたため、last_conversionには30℃の結果が保存され、temp["last"]()で呼び出されています。
動物の鳴き声カウンター
nonlocalを使って、複数のキーを持つ辞書の値を更新する例です。動物の鳴き声をカウントする関数を作成してみましょう。
出力結果
ワン!(1回目)
ニャー!(1回目)
ワン!(2回目)
キリンが鳴きました(1回目)この例では、animal_counter関数が、内側のcount_sound関数を返します。count_sound関数は、nonlocal sounds宣言によって、外側のsounds辞書を直接操作できます。my_animal_counter("イヌ")のように呼び出すと、sounds辞書の"イヌ"キーの値がチェックされ、存在しなければ0で初期化された後、1が加算されます。"イヌ"を2回呼び出すと、カウントが2になっていることが分かります。
買い物カゴの実装
nonlocalを使用して、オンラインショップの買い物カゴ(辞書)に商品と数量を追加する機能を実装してみましょう。
出力結果
リンゴを3個カゴに追加しました
バナナを2個カゴに追加しました
{'リンゴ': 3, 'バナナ': 2}この例では、add_item関数がnonlocal items宣言によって、外側のitems辞書を直接操作しています。add_itemが呼び出されるたびに、items辞書に商品が追加されたり、数量が更新されたりします。get_items関数は、更新された最新のitems辞書を返します。
まとめ
Pythonのnonlocal文は、ネスト関数内で外側関数のスコープにある変数を変更するための便利な機能です。この記事では、nonlocalの基本概念から実用的な使用例まで幅広く解説しました。
nonlocal文が活躍する場面は次のようなケースです。
nonlocal文が活躍する場面
- クロージャ(関数閉包)を実装するとき
- 関数内で状態(データ)を保持したいとき
- カウンター関数など、呼び出すたびに値が変わる関数を作るとき
nonlocal文を用いる上で、押さえておきたいポイントを覚えておきましょう。
重要なポイント
- global文とは違い、外側の「関数」の変数を対象とする
- nonlocalで指定する変数は、外側の関数で定義済みである必要がある
- nonlocal x, yのようにカンマ区切りで複数指定もできる
nonlocal文を理解することで、よりすっきりと整理されたコードが書けるようになります。特に状態を持つ関数をシンプルに実装したい場合に役立つでしょう。ただし、使いすぎるとコードの読みやすさが下がる可能性もあるため、必要な場面で適切に活用することがポイントです。