名前,変数名,シンボル
【 目次 】
名前とか変数名とかシンボルとか,モジュールのグローバルシンボルテーブルとかいろいろな言葉が出てきてわかりずらかったのでまとめてみた。
名前,変数名,シンボル
実行モデル — Python 2.7ja1 documentationによると、
名前 (name) とは、オブジェクトを参照するものを指します。名前への束縛 (name binding) 操作を行うと、名前を導入できます。プログラムテキスト中に名前が出現するたびに、その名前が使われている最も内側の関数ブロック中で作成された 束縛 (binding) を使って名前の参照が行われます。
とある。
公式ドキュメントって、どうも、抽象的でわかりずらい。
pythonのドキュメントではクラス名,関数名や変数名を総称して、名前とかシンボルと呼んでいるよう。
pythonの組込み関数にはシンボルの集合を返す関数がいくつかある。
- locals関数
- globals関数
- dir関数
- vars関数
locals と globals
組み込み関数globals は
現在のグローバルシンボルテーブルを表す辞書を返します。
とある。
一方、locals
は
現在のローカルシンボルテーブルを表す辞書を更新して返します。
とある。
6. モジュール — Python 2.7ja1 documentationによると
各々のモジュールは、自分のプライベートなシンボルテーブルを持っていて、モジュールで定義されている関数はこのテーブルをグローバルなシンボルテーブルとして使います。
pythonのグローバルスコープはモジュールごとに独立していて、モジュールのトップレベルのスコープの事を指すみたい。
「モジュールのトップレベル」というのはわかりやすく言えば関数やクラスの外側にあるという事。
グローバルシンボルテーブルというのはそのモジュールのトップレベルでシンボル(有効なクラス名,関数名や変数名)のリスト(集合), ローカルシンボルテーブルというのは関数内などのローカススコープで有効なシンボルのリストの事らしい。
変数のスコープとか名前空間とかは、これらの関数が返すシンボルテーブルを調べる事でわかる。
Pythonが有効な名前を探す順序は
- 8.5. locals と globals - Dive into Python 5.4. (JAPANESE)
Python は、変数を追跡し続けるために、名前空間と呼ばれるものを用いている。 名前空間は、単に辞書のようなものであり、そのキーは「変数名」、値は「変数の値」である。 実際、すぐ確認できるのだが、Python の辞書と同じように名前空間にアクセスすることができる。
locals関数
locals関数を使うとのその関数内やクラス,メソッド内でのローカルなシンボルの辞書を取得できる。
以下は関数内やクラス内でlocals関数がどのような値を返すかを調べるコードの例である。
locals関数の例
#coding: UTF-8 u"locals関数サンプルモジュール" def print_dict(label,dict): print "*** %s ***" % label for key, value in dict.items(): if key != "__builtins__": print "\t%s=%s" % (key, value) a = u"グローバルな変数" def func(arg): u"func関数" b = u"ローカルな変数" print_dict(u"関数内", locals()) class MyClass: u"MyClassクラス" c = u"クラス内の変数" def func(self,arg): u"MyClassクラスのfuncメソッド" c = u"メソッド内でローカルな変数" print_dict(u"メソッドレベル", locals()) print_dict(u"クラス内トップレベル", locals()) print_dict(u"モジュールトップレベル", locals()) func(u"関数引数arg") MyClass().func(u"メソッド引数arg")
冒頭で定義しているprint_dict関数は辞書のkeyと値を出力するための関数である。
実行結果
*** クラス内トップレベル ***
c=クラス内の変数
__module__=__main__
__doc__=MyClassクラス
func=<function func at 0x025C0430>
*** モジュールトップレベル ***
a=グローバルな変数
print_dict=<function print_dict at 0x025C0470>
__file__=ソースファイルのパス
__package__=None
func=<function func at 0x025C04B0>
MyClass=__main__.MyClass
__name__=__main__
__doc__=locals関数サンプルモジュール
*** 関数内 ***
b=ローカルな変数
arg=関数引数arg
*** メソッドレベル ***
c=メソッド内でローカルな変数
self=<__main__.MyClass instance at 0x025BFC88>
arg=メソッド引数arg
globals関数
globals関数はモジュール内のグローバルな名前の辞書を返す。
モジュールトップレベルではlocals関数の返す辞書オブジェクトはglobals関数が返す辞書オブジェクトと同じみたい。
globals関数の例
#coding: UTF-8 u"globals関数サンプルモジュール" def print_dict(label,dict): print "*** %s ***" % label for key, value in dict.items(): if key != "__builtins__": print "\t%s=%s" % (key, value) a = u"グローバルな変数" def func(arg): u"func関数" b = u"ローカルな変数" print_dict(u"関数内", locals()) class MyClass: u"MyClassクラス" c = u"クラス内の変数" def func(self,arg): u"MyClassクラスのfuncメソッド" c = u"メソッド内でローカルな変数" print_dict(u"メソッドレベル", locals()) print_dict(u"クラス内トップレベル", locals()) print_dict(u"モジュールトップレベル", locals()) func(u"関数引数arg") MyClass().func(u"メソッド引数arg")
実行結果
*** クラス内トップレベル ***
c=クラス内の変数
__module__=__main__
__doc__=MyClassクラス
func=<function func at 0x02442430>
*** モジュールトップレベル ***
a=グローバルな変数
print_dict=<function print_dict at 0x024424B0>
__file__=ソースファイルのパス
__package__=None
func=<function func at 0x024424F0>
MyClass=__main__.MyClass
__name__=__main__
__doc__=globals関数サンプルモジュール
*** 関数内 ***
b=ローカルな変数
arg=関数引数arg
*** メソッドレベル ***
c=メソッド内でローカルな変数
self=<__main__.MyClass instance at 0x02441C88>
arg=メソッド引数arg
実行する順番によって辞書の内容は異なる。
インタープリターは実行順に解釈されるので、名前が宣言される度にlocals関数,globals関数を実行した時の辞書の内容が更新される事になる。
辞書の内容が更新される
#coding: UTF-8 def print_dict(label,dict): print "*** %s ***" % label for key, value in dict.items(): print "\t%s=%s" % (key, value) def func(): a = u"変数a" print_dict(u"変数bを宣言する前のlocals関数", locals()) b = u"変数b" print_dict(u"モ変数bを宣言した後のlocals関数", locals()) func()
実行結果
*** 変数bを宣言する前のlocals関数 *** a=変数a *** モ変数bを宣言した後のlocals関数 *** a=変数a b=変数b
変数を宣言すると、辞書にも名前が追加される。
import文とglobals
グローバルはあくまでモジュール内(pythonソースファイル内)でグローバルであるという事。
別のモジュールのシンボルを使う場合はimport文を使って他のモジュールのシンボルを取り込む必要がある。
global 文
関数内でグローバル変数に値を代入するにはglobal文を使って変数名を指定する必要がある。
参照するだけならglobal文は必要無い。
ややこしい事に、global文を使わずに、グローバル変数に値を代入するとローカル変数に値を代入した事になってしまう。
dir関数 - オブジェクトに定義されている名前のリストを取得
dir関数を使うとオブジェクトに定義されている名前のリストを取得できる。
locals関数,globals関数と異なりdir関数は辞書ではなくリストを返す。
名前のリストとは言い換えると、オブジェクトに属するメソッドや属性のリストと考える事ができる。
オブジェクトの属性値に「オブジェクト.名前」でアクセスできるという事。
引数に関数やクラス,変数を指定すると、その関数オブジェクト,クラスオブジェクト,変数オブジェクトに属する名前のリストを取得できる。
6. モジュール — Python 2.7ja1 documentation
組込み関数 dir() は、あるモジュールがどんな名前を定義しているか調べるために使われます。 dir() はソートされた文字列のリストを返します。
... 変数、モジュール、関数、その他の、すべての種類の名前をリストすることに注意してください。
引数を指定せずにdir関数を実行すると、その実行位置に応じたアクセス可能な名前のリストを取得できる。
すなわち、モジュールレベルで引数を指定せずにdir関数を実行するとモジュール(オブジェクト)に定義されている名前のリストを取得,
関数内やメソッド内で実行すると関数内やメソッド内でアクセスできる名前のリストを取得できると推察される。
dir関数の例
#coding: UTF-8 a = u"グローバル変数" def func(): b = u"ローカル変数" print "*** dir() - 関数内 ***" for symbol in dir(): print "\t",symbol,"->", eval(symbol) class Person: a = u"クラス変数" def __init__(self,name,age): self.name = name self.age = age def ShowMe(self): print self.name, u"は", self.age, u"才です。" person = Person(u"愚鈍人",999) import sub # 変数を引数にとるとdir関数は print "*** dir(a) ***" for symbol in dir(a): full_symbol = "a." + symbol print "\t",full_symbol,"->", eval(full_symbol) # 関数を引数にとると print "*** dir(func) ***" for symbol in dir(func): full_symbol = "func." + symbol print "\t",full_symbol,"->", eval(full_symbol) # クラスを引数にとると print "*** dir(Person) ***" for symbol in dir(Person): full_symbol = "Person." + symbol print "\t",full_symbol,"->", eval(full_symbol) # クラスのインスタンスを引数にとると print "*** dir(person) ***" for symbol in dir(person): full_symbol = "person." + symbol print "\t",full_symbol,"->", eval(full_symbol) # モジュールオブジェクトを引数にとると print "*** dir(sub) ***" for symbol in dir(sub): full_symbol = "sub." + symbol print "\t",full_symbol,"->", eval(full_symbol) # 関数無いでdir関数を引数無しで使うと func() # モジュールのトップレベルでdir関数を引数無しで使うと print "*** dir() - モジュールトップレベル ***" for symbol in dir(): print "\t",symbol,"->", eval(symbol)
sub.py
#coding: UTF-8 a = u"subモジュールの変数" def func(): print u"subモジュールの関数funcを実行" class MyClass: def func(self): print u"subモジュールの関数MyClassのfuncメソッドを実行"
実行結果
*** dir(a) *** a.__add__ -> <method-wrapper '__add__' of unicode object at 0x023A7500> a.__class__ -> <type 'unicode'> a.__contains__ -> <method-wrapper '__contains__' of unicode object at 0x023A7500> ... 途中略 a.find -> <built-in method find of unicode object at 0x023A7500> a.format -> <built-in method format of unicode object at 0x023A7500> a.index -> <built-in method index of unicode object at 0x023A7500> ... 途中略 *** dir(func) *** func.__call__ -> <method-wrapper '__call__' of function object at 0x02397870> func.__class__ -> <type 'function'> func.__closure__ -> None ... 途中略 func.func_dict -> {} func.func_doc -> None func.func_globals -> {'a': u'\u30b0\u30ed\u30fc\u30d0\u30eb\u5909\u6570', ... 途中略 func.func_name -> func *** dir(Person) *** Person.ShowMe -> <unbound method Person.ShowMe> Person.__doc__ -> None Person.__init__ -> <unbound method Person.__init__> Person.__module__ -> __main__ Person.a -> クラス変数 *** dir(person) *** person.ShowMe -> <bound method Person.ShowMe of <__main__.Person instance at 0x0239E2D8>> person.__doc__ -> None person.__init__ -> <bound method Person.__init__ of <__main__.Person instance at 0x0239E2D8>> person.__module__ -> __main__ person.a -> クラス変数 person.age -> 999 person.name -> 愚鈍人 *** dir(sub) *** sub.MyClass -> sub.MyClass sub.__builtins__ -> {'bytearray': <type 'bytearray'>, ... 途中略 sub.__doc__ -> None sub.__file__ -> ソースファイルのパス(sub.pyc) sub.__name__ -> sub sub.__package__ -> None sub.a -> subモジュールの変数 sub.func -> <function func at 0x02397AB0> *** dir() - 関数内 *** b -> ローカル変数 *** dir() - モジュールトップレベル *** Person -> __main__.Person __builtins__ -> <module '__builtin__' (built-in)> __doc__ -> None __file__ -> ソースファイルのパス __name__ -> __main__ __package__ -> None a -> グローバル変数 full_symbol -> sub.func func -> <function func at 0x02397870> person -> <__main__.Person instance at 0x0239E2D8> sub -> <module 'sub' from 'ソースファイルのパス(sub.pyc)'> symbol -> symbol
dir関数の引数として変数aを指定すると変数aよりアクセスできる属性やメソッドを知る事ができる。
変数aがunicode文字列を保持しているので、変数aはunicode文字列型のインスタンスという事になり、文字列型の保持する属性やメソッドのリストが返されていると思われる。
関数オブジェクトを引数とした場合、関数オブジェクトにはいろいろな名前の属性が生成されているのがわかる。
クラスを引数とした場合とクラスのインスタンスを引数とした場合の結果を比較すると、クラスのインスタンスではクラス自体のメンバーの他にインスタンスのメンバーにもアクセスできるのがわかる。
subモジュールをimportした事によって変数subを使ってsubモジュールオブジェクトのグローバル変数にアクセスできる事がわかる。
引数無しのdir関数の関数内ではローカルなシンボルのリストをモジュールトップレベルではグローバルな名前のリストを返すのがわかる。
これはまた、関数内,モジュールトップレベルでそれをれlocals関数を実行した時に返される辞書のキーのリストと同じようだ。
vars関数
locals関数やglobals関数と同様に、vars関数もシンボルの辞書を返す。
vars関数は引数で指定されたオブジェクトの属性を辞書として返す。
#coding: UTF-8 def print_dict(label,dict): print "*** %s ***" % label for key, value in dict.items(): print "\t%s=%s" % (key, value) class Person: a = u"クラス変数" def __init__(self,name,age): self.name = name self.age = age def ShowMe(self): print self.name, u"は", self.age, u"才です。" person = Person(u"愚鈍人",999) person.ShowMe() print "Person.a=",Person.a print_dict("vars(person)",vars(person))
実行結果
愚鈍人 は 999 才です。 Person.a= クラス変数 *** vars(person) *** age=999 name=愚鈍人
引数無しで実行するとlocals() と同じ辞書を返すみたい。
引数無しで実行するとlocals() と同じ辞書を返す
a = u"グローバル変数" def func(): b = u"ローカル変数" print_dict(u"*** 関数内のvars関数 ***",vars()) if vars() is locals(): print u"関数内のvars関数とlocals関数は同じオブジェクトを返す" else: print u"関数内のvars関数とlocals関数は異なるオブジェクトを返す" func() print_dict(u"*** モジュールトップレベルのvars(関数) ***",vars()) if vars() is locals(): print u"モジュールトップレベルのvars関数とlocals関数は同じオブジェクトを返す" else: print u"モジュールトップレベルのvars関数とlocals関数は異なるオブジェクトを返す"
実行結果
*** *** 関数内のvars関数 *** *** b=ローカル変数 関数内のvars関数とlocals関数は同じオブジェクトを返す *** *** モジュールトップレベルのvars(関数) *** *** a=グローバル変数 print_dict=<function print_dict at 0x026093B0> __builtins__=<module '__builtin__' (built-in)> __file__=ソースファイル名 __package__=None func=<function func at 0x026524F0> __name__=__main__ __doc__=None モジュールトップレベルのvars関数とlocals関数は同じオブジェクトを返す
vars関数の引数にはオブジェクトを指定するが、引数で指定したオブジェクトに__dict__メンバーが存在しない場合は以下のエラーになる。
TypeError: vars() argument must have __dict__ attribute
__dict__メンバーが存在しないのでエラーになる例
a=u"グローバル変数" print vars(a) # 変数オブジェクトaには__dict__メンバーが無い
__dict__メンバーについては
del文 - シンボルの削除
シンボルはdel文
を使って削除できる。
次の例は、文字列変数をいったん定義した後、del文を使って削除する例である。
#coding: UTF-8 str = u"削除予定のシンボル" print dir() del str print dir()
実行結果をみるとstrシンボルが削除されているのがわかる。
実行結果
['__builtins__', '__doc__', '__file__', '__name__', '__package__', 'str']
['__builtins__', '__doc__', '__file__', '__name__', '__package__']
delattr という組み込み関数もある。