このメッセージは多くの例で少し長くなりますが、Python 2.7 での変数と属性ルックアップの完全なストーリーをよりよく理解するのに役立つことを願っています。
コードブロック(モジュール、クラス定義、関数定義など)と変数バインディング(代入、引数の宣言、クラスと関数の宣言、for ループなど)
ドットなしで呼び出すことができる名前には変数という用語を使用し、オブジェクト名で修飾する必要がある名前には属性という用語を使用しています (オブジェクト obj の属性 x の obj.x など)。
Python には、すべてのコード ブロックに対して 3 つのスコープがありますが、関数は次のとおりです。
- ローカル
- グローバル
- ビルトイン
関数専用の Python には 4 つのブロックがあります (PEP 227 によると)。
- ローカル
- 囲み関数
- グローバル
- ビルトイン
変数をバインドしてブロック内で見つけるためのルールは非常に単純です。
- ブロック内のオブジェクトに変数をバインドすると、変数がグローバルに宣言されていない限り (その場合、変数はグローバル スコープに属します)、この変数はこのブロックに対してローカルになります。
- 変数への参照は、すべてのブロックに対してルール LGB (ローカル、グローバル、ビルトイン) を使用して検索されますが、関数は
- 変数への参照は、関数に対してのみルール LEGB (ローカル、エンクロージング、グローバル、ビルトイン) を使用して検索されます。
このルールを検証し、多くの特殊なケースを示す例を教えてください。それぞれの例について、私の理解を示します。間違っている場合は修正してください。最後の例については、結果がわかりません。
例 1:
x = "x in module"
class A():
print "A: " + x #x in module
x = "x in class A"
print locals()
class B():
print "B: " + x #x in module
x = "x in class B"
print locals()
def f(self):
print "f: " + x #x in module
self.x = "self.x in f"
print x, self.x
print locals()
>>>A.B().f()
A: x in module
{'x': 'x in class A', '__module__': '__main__'}
B: x in module
{'x': 'x in class B', '__module__': '__main__'}
f: x in module
x in module self.x in f
{'self': <__main__.B instance at 0x00000000026FC9C8>}
クラスのネストされたスコープはなく (ルール LGB)、クラス内の関数は、修飾名 (この例では self.x) を使用しないとクラスの属性にアクセスできません。これは PEP227 で詳しく説明されています。
例 2:
z = "z in module"
def f():
z = "z in f()"
class C():
z = "z in C"
def g(self):
print z
print C.z
C().g()
f()
>>>
z in f()
z in C
ここで、関数内の変数は LEGB ルールを使用して検索されますが、クラスがパスにある場合、クラスの引数はスキップされます。繰り返しますが、これは PEP 227 が説明していることです。
例 3:
var = 0
def func():
print var
var = 1
>>> func()
Traceback (most recent call last):
File "<pyshell#102>", line 1, in <module>
func()
File "C:/Users/aa/Desktop/test2.py", line 25, in func
print var
UnboundLocalError: local variable 'var' referenced before assignment
Python などの動的言語では、すべてが動的に解決されることが期待されます。しかし、これは関数には当てはまりません。ローカル変数はコンパイル時に決定されます。PEP 227 および http://docs.python.org/2.7/reference/executionmodel.htmlは、この動作をこのように説明しています
「名前バインディング操作がコード ブロック内のどこかで発生した場合、ブロック内の名前のすべての使用は、現在のブロックへの参照として扱われます。」
例 4:
x = "x in module"
class A():
print "A: " + x
x = "x in A"
print "A: " + x
print locals()
del x
print locals()
print "A: " + x
>>>
A: x in module
A: x in A
{'x': 'x in A', '__module__': '__main__'}
{'__module__': '__main__'}
A: x in module
しかし、PEP227 のこのステートメントは、「コード ブロック内の任意の場所で名前バインディング操作が発生した場合、ブロック内の名前のすべての使用は、現在のブロックへの参照として扱われます。」コードブロックがクラスの場合は間違っています。また、クラスについては、コンパイル時ではなく、実行時にクラスの名前空間を使用してローカルの名前バインディングを行うようです。その点で、PEP227 と Python doc の実行モデルは誤解を招きやすく、一部が間違っています。
例 5:
x = 'x in module'
def f2():
x = 'x in f2'
def myfunc():
x = 'x in myfunc'
class MyClass(object):
x = x
print x
return MyClass
myfunc()
f2()
>>>
x in module
このコードの私の理解は次のとおりです。命令 x = x は、最初に式の右手 x が参照しているオブジェクトを調べます。その場合、オブジェクトはクラス内でローカルに検索され、次にルール LGB に従って、文字列 'x in module' であるグローバル スコープで検索されます。次に、MyClass へのローカル属性 x がクラス ディクショナリに作成され、文字列オブジェクトを指します。
例 6:
ここで、説明できない例を示します。これは例 5 に非常によく似ています。ローカルの MyClass 属性を x から y に変更しているだけです。
x = 'x in module'
def f2():
x = 'x in f2'
def myfunc():
x = 'x in myfunc'
class MyClass(object):
y = x
print y
return MyClass
myfunc()
f2()
>>>
x in myfunc
その場合、MyClass の x 参照が最も内側の関数で検索されるのはなぜですか?