[Python入門]関数のローカル変数とスコープPython入門(3/3 ページ)

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

名前空間

 スコープは「変数や関数などが存在する場所」を示すと同時に、あるコード位置である名前を使ったときに「その名前を解決して対応する値(オブジェクト)を取り出すときにたどる道のりや視野」のようなものでもある。何度も述べているが「ローカルスコープ→グローバルスコープ→ビルトインスコープ」という順序で名前は検索され、より近い方にある名前が優先して使用される。そして、これを実現するために使われているのが、Pythonの「名前空間」だ。

 「名前空間」とは「名前」と「その値」を関連付けるものだ(一般には既に何度か触れている「辞書」と呼ばれるデータ構造を使って実装されている)。名前空間はスコープに対応し、「ビルトイン名前空間」「グローバル名前空間」「関数のローカル名前空間」などがある。

 Pythonのドキュメント「Python のスコープと名前空間」によれば、名前空間が作成されるタイミングについて次のようになる。

  • ビルトイン名前空間:Pythonインタープリタの起動時に作成され、インタープリタ終了時に削除される
  • グローバル名前空間:モジュール読み込み時に作成され、多くの場合はインタープリタ終了時に削除される
  • ローカル名前空間:関数が呼び出されるタイミングで作成され、終了時に削除される

 本稿の時点ではモジュールについてはきちんと説明をしていないので、理解が難しいかもしれないが、Jupyter Notebookなどのセル(に入力する変数定義など)は「__main__」という名前のモジュールに含まれることになっている。これはPythonが暗黙的に用意するグローバル変数「__name__」の値を調べると分かる。グローバル変数__name__の値は、現在実行中のモジュール名となるので、セルに以下を入力して、実行してみよう。

__name__

現在のモジュール名を調べる

 セルに「__name__」とだけ入力して実行すれば、その評価結果が得られる。実行結果を以下に示す。

現在のモジュール名は「__main__」 現在のモジュール名は「__main__」

 すると、「__main__」と表示された。つまり、現在、セルにコードを入力して実行しているモジュールは「__main__」モジュールであり、セルに以下のコードを入力すれば、そこで定義している変数some_varはモジュールのローカルスコープ(とはモジュールのグローバルスコープ)で定義されるグローバル変数となる。

some_var = 'some_var'

__main__モジュールで変数some_varを定義

ローカル名前空間

 あるスコープにどんな名前が存在しているかは、対応する名前空間を調べると分かる。関数のローカルスコープで定義された名前は、対応するローカル名前空間で管理されている。これを調べるのに使えるのが、Pythonに組み込みのlocals関数あるいはdir関数だ。

 locals関数は現在のローカルスコープで定義されている名前とその値の組を要素とする辞書を返すものだ(辞書とは「キー: その値」という組を格納するためのデータ構造)。dir関数は引数なしで呼び出すと、現在のローカルスコープに存在する名前を要素とするリストを返す(リストは任意の個数の任意の種類のデータを格納するデータ構造)。

 例えば、次のコードを考えてみよう(ローカル変数aの値を表示するprint関数呼び出しはコメントアウトした)。

def myfunc():
    a = 'Python'
    # print('a:', a)
    print(locals())

myfunc()
print(locals())

関数のローカルスコープで定義されている名前と、グローバルスコープで定義されている名前を表示

 このコードでは、myfunc関数のローカルスコープと、モジュールのトップレベル(グローバルスコープ)でlocals関数を呼び出して、その結果を画面に表示している。表示されるものにどんな違いがあるかを見てみよう(ここでは、グローバルスコープでのlocals関数の呼び出し結果がシンプルになるように、Pythonの実行環境をリセットしている)。

実行結果 実行結果

 最初の「{'a': 'Python'}」という表示がmyfunc関数のローカルスコープで定義されている名前と、その値だ。非常にシンプルで、ローカル変数aとその値である文字列'Python'の組だけが格納されていることが分かる。

 次の表示がモジュールのトップレベルで定義されている名前とその値が格納されている辞書の内容だ。直前に定義したグローバル変数some_var(その値は文字列'some var')や自分で定義したmyfunc関数なども見られるが、それ以外にも多くのものが格納されていることが分かる。これらはPythonのインタープリタや対話環境のセットアップ時に自動的に作成されるものだ。

 コードの実行位置によって、ローカルスコープならびにローカル名前空間に存在するもの(名前)が異なることが分かったはずだ。myfunc関数の実行時に「a」という名前が使われたら、まずはローカル名前空間にある「a」(最初の「{'a': 'Python'}」にある「a」)が発見されることも想像できるはずだ。これがスコープの説明で話した名前解決で実際に行われていることだ。

 もう一つのdir関数は、引数なしで呼び出すと、現在のローカルスコープ(関数内なら関数のローカルスコープ、モジュールのトップレベルならモジュールのローカルスコープ=グローバルスコープ)に存在する名前を含んだリストを戻す。興味のある方は、先ほどのコードでlocals関数を呼び出しているところを、dir関数呼び出しに変えて実行してみよう。

dir関数を引数なしで呼び出すと、現在のローカルスコープで定義されている名前だけが返される dir関数を引数なしで呼び出すと、現在のローカルスコープで定義されている名前だけが返される

グローバル名前空間

 ローカルスコープに存在する名前を調べるには、今述べたlocals関数が使える。これと同様に、グローバルスコープに存在する名前を調べるには、globals関数が使える。セルにglobals関数呼び出しを書いて実行してみたところが以下だ。

globals関数の呼び出し結果(出力は途中で省略) globals関数の呼び出し結果(出力は途中で省略)

 本稿では「モジュールのトップレベルのローカルスコープとグローバルスコープは同じものになる」といった話をしていたが、これは「両者が同じ名前空間を参照する」という意味でもある。そこで、locals関数呼び出しの結果とglobals関数の呼び出しの結果を比べてみよう。といっても、上に示したようにその出力は非常に長いものになる。そこで次のコードを実行してみよう。

locals() == globals()

locals関数の戻り値とglobals関数の戻り値が等しいかを比較演算子「==」で比べる

 locals関数の戻り値とglobals関数の戻り値が等しいかを「==」演算子で比べれば、それらが同じかどうかが分かるはずだ。これを実行すると次のようになる。

比較演算の結果がTrueなので、両関数の戻り値は等しい 比較演算の結果がTrueなので、両関数の戻り値は等しい

 「==」演算子による比較の結果はTrueなので、両者が同じものを参照していることが分かったはずだ。

 これに対して、関数内でglobals関数を呼び出したときには、globals関数はその関数を含んでいるグローバルスコープにある名前(とその値)が格納された辞書を返すので、locals関数とは結果が異なるものになる。これも確認してみよう。

def myfunc():
    a = 'Python'
    print('locals:', locals())
    print('globals:', globals())

myfunc()

関数内でのlocals関数の戻り値とglobals関数の呼び出し

 これを実行すると、次のようになる。

関数内部ではlocals関数の戻り値とglobals関数の戻り値は異なる 関数内部ではlocals関数の戻り値とglobals関数の戻り値は異なる

 myfunc関数内では、ローカル名前空間には先ほども見たようにローカル変数aだけがあり、グローバル名前空間にはより多くの名前が見られる(これらはモジュールのトップレベルと同じものだ)。

まとめ

 本稿では、Pythonの関数におけるローカル変数をスタート地点として、スコープや名前空間について見てきた。スコープと名前空間は密接な関係にあり、コードの特定箇所で名前を使用したときに、その名前を実際の値(オブジェクト)へと解決するには、スコープをたどりながら、各スコープの名前空間を検索していく。こうした動作自体は、すぐには覚えておく必要はないが、スコープには種類があり、最も内側のスコープ(ローカルスコープ)から最も外側のスコープ(ビルトインスコープ)へと検索が順次行われること、その過程で内側に存在する名前が使われることと、内側のスコープで定義したものは外側のスコープからは見えないことはよく覚えておこう。

 次回は、オブジェクトとしての関数、ラムダ式など、関数について積み残している話題を取り上げる。

今回のまとめ:関数とスコープ

  • 関数内で定義する変数のことを「ローカル変数」と呼ぶ
  • ローカル変数は、関数の実行時にのみ利用可能かつ関数外部からは利用できない
  • モジュールのトップレベルで定義する変数のことを「グローバル変数」と呼ぶ
  • 関数からはモジュールのグローバル変数の値を「参照」できるが、通常は「変更」できない
  • 関数内でグローバル変数と同じ名前の変数に代入を行うと、ローカル変数の定義となる
  • グローバル変数の値を、関数内で変更するときには、「global文」を使ってグローバル宣言を行う
  • 「スコープ」とは「変数や関数の有効範囲」であり、コードの特定箇所で名前を使用したときに、それがどのような値に解決されるかの筋道でもある
  • 関数は独自のローカルスコープを形成する
  • モジュールは独自のグローバルスコープ(モジュールスコープ)を形成する
  • 組み込み関数などを格納するビルトインスコープもある
  • 名前の解決ではローカルスコープ→グローバルスコープ→ビルトインスコープの順に名前が検索される
  • 各スコープには対応する「名前空間」があり、ここでそのスコープに属する名前が管理される
  • ローカルスコープに存在する名前を調べるには、locals関数やdir関数が使える
  • グローバルスコープに存在する名前を調べるには、globals関数が使える

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

Python入門

前のページへ 1|2|3       

Copyright© Digital Advantage Corp. All Rights Reserved.

スポンサーからのお知らせPR

注目のテーマ

AI for エンジニアリング
「サプライチェーン攻撃」対策
1P情シスのための脆弱性管理/対策の現実解
OSSのサプライチェーン管理、取るべきアクションとは
Microsoft & Windows最前線2024
システム開発ノウハウ 【発注ナビ】PR
あなたにおすすめの記事PR

RSSについて

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

メールマガジン登録

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