名前空間とスコープについて学んでいきましょう。
名前とは
名前空間について学ぶ前に、名前について理解していきましょう。
名前とは一言で行くと、参照のことです。
参照という考え方については、メモリとアドレスという記事や、変数という記事で説明していますので、参考にして下さい。
Pythonでは、変数にはアドレスの情報が入っています。
また、関数の名前にもアドレスの情報が入っています。これは関数という記事で説明していますので、参考にして下さい。
つまり、名前というのは参照のことであり、参照というのはメモリ上のアドレスのことであり、そのアドレスに文字列や関数といったデータ(オブジェクト)が格納されています。
そして、名前はデータが入った「場所」を示しているというイメージを持つと、名前空間が理解しやすくなります。
Pythonにおける名前空間とは
次に、名前空間について見ていきましょう。
名前空間の定義は、「名前からオブジェクトへの対応付け」ですが、これでは何となくイメージがわきづらいと思います。
違う表現をすると、名前空間というのは、他の名前(変数や関数の名前)と重複せず、単一の名前として使うことができる範囲のこと、ということができます。
これでもイメージが湧きづらいと思いますので、例を挙げて考えてみましょう。
例えば、ある小学校について考えてみましょう。この小学校の1年1組には、下の名前が一郎という生徒が一人いるとします。
このクラスの中では、一郎と呼べば、一郎が返事をします。
しかし、この小学校には1年3組にも一郎がいます。1年生が全員集まった場で一郎を呼ぶためには、1組の一郎か、3組の一郎か、どちらの一郎かを指定しなければいけません。
名前空間も同じです。その名前空間の中では、名前をユニーク(単一で重複しない)なものとして使うことができる空間のことを名前空間と言うのです。
さらに、名前の定義に戻って考えてみましょう。
先程、名前は参照(メモリ上のある場所)とお伝えしました。
つまり、メモリ上の場所は名前で関連付けられています。例えば、aという名前がメモリのxx番地を、bという名前がyy番地を参照している。といった形です。
そして、aという名前でxx番地を参照できるのは名前空間AAの中だけである。bという名前でyy番地を参照できるのは名前空間BBだけである。といった形で、名前と番地の結びつきを使えるhのことを名前空間というのです。
Pythonにおける名前空間の具体例
実際のコードで名前空間の範囲に対するイメージをふくらませていきましょう。
コード-namespace.py
1 def sample():
2 number = 1
3 string = 'abc'
4 floating = 9.9
5 def plus():
6 return
7 print(locals())
8 return
アウトプット{'plus': <function sample.<locals>.plus at 0x7f7c03e18510>,
'floating': 9.9,
'stringdd': 'abc',
'number': 1}
コードの解説(1) sampleという関数の中で、3つの変数を定義しており、さらにplusという関数を定義しています。
(2) 7行目で、localsという関数を実行しています。これは、locals()が存在している名前空間で参照できる変数や関数を返します(詳しい内容は後ほど説明しています)。
(3) 出力結果として、plus関数、変数number、string、floatingが表示されたことが分かります。
つまり、sampleという関数の中で(ブロックで)一つの名前空間ができており、それは具体的にはplus、number、string、floatingの4つということです。
次から、イメージで名前空間への理解を深めていきましょう。
Pythonにおける名前空間のイメージ
Pythonにおいて名前空間がどのように構成されているのかをみていきましょう。
Pythonにおける名前空間は大きく以下のような範囲があります
global名前空間
まずはglobal名前空間です。
global名前空間は、関数やif文などを書く時に作られるブロックの外にあり、どこからでも参照することが可能な空間です。
適切な例えか分からないのですが、ウェブサイトであればログイン前のトップページのようなイメージと言えるかもしれません。
Local名前空間
つぎはlocal名前空間です。
local名前空間は、その名前の通り個別に作られる名前空間です。関数やif文、クラス定義をした時に作成されるブロックの中で使うことができるという表現が分かりやすいかもしれません。
ウェブサイトの例えで言うと、ログインした後に表示される個人情報ページや、購入履歴ページといえるかもしれません。
Builtin名前空間
Builtin名前空間は、Pythonにあらかじめ組み込まれている名前空間です。モジュールの記事の中で__name__=__main__というコードをお伝えしましたが、こういった変数は定義をせずとも自動的に使うことができますよね。
このような変数などを参照することができる空間のことをbuiltin名前空間と呼びます。
このあたりの詳細は後ほどスコープの所で説明しますので、まずはこのような分類がされているんだな、というイメージをもって貰えれば大丈夫です。
名前空間での名前の使い分け
名前の使い分けについて具体例でみていきましょう。
例えば、ビルトイン名前空間の中でnum = 5という名前を使っても、ローカル名前空間の中でnum = 10という名前を使って構いません。
つまり、ビルトイン名前関数でnumを呼び出せば5が表示され、ローカル名前空間の中で呼びされれば10が表示されるのです。
名前空間で使える名前を確認する方法
ここから、具体的なコードで名前空間をみていきましょう。
グローバル名前空間はglobals()で確認することができます。実際に見てみましょう。
コードprint(globals())
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x7f8de70c17f0>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'new2.py', '__cached__': None}
沢山の名前空間があることが分かるかと思います。
次に、a = 10というコードを書いた上でglobals()を実行してみましょう。
コードa = 10
print(globals())
アウトプット{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x7f07787c17f0>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'new2.py', '__cached__': None, 'a': 10}
実行結果を見てみると、最後に'a': 10いう記載が入っていることが分かるかと思います。
これは、aという名前(参照)で10という整数オブジェクトにアクセスすることができる。ということを示しています。
名前空間は、生まれて、消えてを繰り返している
また、名前空間は都度「生成と消滅」が繰り返されているということも頭に入れておくと良いでしょう。
例えば、関数によって作成された名前空間(ブロック)は、ブロックのコードの実行が終わると名前空間もなくなってしまいます(名前を使って参照することができなくなります。)
これは、使わない名前を残しておくとメモリの消費につながるということと、名前が残っていると他の名前との衝突が起こってしまう可能性が高くなってしまうというのが理由です。
普段コードを書く時にこの「生成と消滅」を意識する場面はほとんどありませんが、一応このような特徴があるということは頭に入れておいても良いかもしれません。
スコープ
次に、スコープについてみていきましょう。
スコープとは、修飾すること無しに参照することができる名前の範囲のことをいいます。
名前空間は、名前とオブジェクトの対応付けでした(名前空間でも範囲という言葉を使って説明していますが。。)一方、スコープは範囲を示しています。といってもこれでもイメージがわきづらいですよね。
厳密に名前空間と使い分けられている情報源も探してみたのですが、見当たりませんでしたので、名前空間とスコープの言葉の定義を厳密に使い分けようとはしなくても良いでしょう。
スコープにおいて知って置かなければいけないことは、Pythonではスコープ外の変数を参照することができるということです。
これも具体例で考えた方が分かりやすいですので、具体例で考えていきましょう。
1 def outer():
2 number = 10
3 def inner():
4 print(locals())
5 return
6 return
{'number': 10}
inner関数の中で名前空間を出力したところ、'number':10という結果が返ってきました。
これは、inner関数の中の変数は、innner関数より外のouter関数もスコープということです。
では、グローバル変数(グローバル名前空間で定義した変数)の場合、どうなるでしょうか?
これも実際のコードで確かめてみましょう。
コード1 number = 10
2 def outer():
3 def inner():
4 print(locals())
5 return
6 return
アウトプット{}
今回の場合、名前空間には何もされません。
つまり、numberという名前は、innerのスコープ外ということです。
スコープ外でも、参照はできる
次に、スコープと参照について学んでいきましょう。
結論からお伝えすると、Pythonではスコープの外の名前であっても、その名前を参照することができます。
具体的なコードで確認してみましょう。
コード1 number = 10
2 def outer():
3 def inner():
4 print(locals())
5 print(number)
6 return
7 return
アウトプット{}
10
innnerの名前空間にnumberは入っていませんが、その関数の中でnumberをprintしたところ、10という結果を得ることができました。
これは、プログラムを効率よく書くためにPythonに備わっている実装なのです。
名前空間とスコープのまとめ
(1) 名前空間というのは、オブジェクトと名前(変数・関数)の対応付けであり、スコープは、それが有効な範囲。
(2) スコープの外にある名前であっても、参照することは可能。
名前空間とスコープを学んだ後は
Pythonで非常に重要な考え方であるクラスについて学んでいきましょう。