下記の関数定義中の変数barは、関数のブロック内にbarの定義がないためグローバル変数と解釈されます。
def foo():
print(bar)
下記のコードは、fooの呼び出し前にグローバル変数barが定義されているので成功します。
bar = 4
def foo():
print(bar)
foo() # => 4
fooのグローバル変数は、fooのコードオブジェクトの属性co_namesで確認できます。
foo.__code__.co_names # => ('print', 'bar')
ちなみにPythonの公式ドキュメントでは、co_namesはローカル変数名のtupleと書いてありますが、(ローカル変数名と自由変数を除く)名前の間違いです。
下記の関数定義中のbarは、関数のブロック内にbarが定義されているため、ローカル変数と解釈されます。
def foo():
print(bar)
bar = 3
foo.__code__.co_names # => ('print',)
fooのローカル変数は、fooのコードオブジェクトの属性co_varnamesで確認できます。
foo.__code__.co_varnames # => ('bar',)
この解釈は、関数定義が行われたときに決まるため、barの定義の前にbarがあってもグローバル変数とは解釈されず、次のようにエラーになります。
bar = 4
def foo():
print(bar)
bar = 3
foo()
# => UnboundLocalError: local variable 'bar' referenced before assignment
つまり、関数ブロック中の名前は、関数定義の段階でグローバル変数かローカル変数か(もしくは自由変数か)に決まっており、名前への代入時点でグローバル変数からローカル変数にリバインドされるのではありません。
さらに、リストなどの内包表記は新たなスコープを導入しますが、最初のforに対応するイテラブルのみ、外側のスコープで解釈されます。下記の例は成功しますが、リスト内包表記内の3つのbarのうち、一番右のbarはfooのスコープ、すなわちグローバル変数のbarと解釈されるためです。
bar = range(3)
def foo():
print([bar for bar in bar])
foo() # => [0, 1, 2]