11

私は、Pythonの変数バインディングがどのように機能するかを正確に理解しようとしています。これを見てみましょう:

def foo(x):
    def bar():
        print y
    return bar

y = 5
bar = foo(2)
bar()

これは私には合理的と思われる5を印刷します。

def foo(x):
    def bar():
        print x
    return bar
x = 5
bar = foo(2)
bar()

これは奇妙な2を印刷します。最初の例では、Pythonは実行中に変数を検索し、2番目の例ではメソッドが作成されます。なんでそうなの?

明確にするために:これは非常にクールで、私が望むとおりに機能します。ただし、内部バー関数がどのようにコンテキストを取得するかについては混乱しています。内部で何が起こっているのかを理解したいと思います。

編集

ローカル変数の方が優先度が高いことは知っています。私が興味を持っているのは、Pythonが実行中に、以前に呼び出した関数から引数を取得することをどのように知っているかです。barで作成されfoox現在は存在しません。x関数が作成されたときにこれを引数値にバインドしましたか?

4

5 に答える 5

7

2番目の例は、クロージャーと呼ばれるものを実装します。関数は、周囲のコンテキスト、つまり関数からbar変数を参照しています。これは、グローバル変数への参照の前にあります。xfoox

この質問も参照してください(Pythonに関連しているため)クロージャについて説明できますか?

于 2010-11-27T15:23:46.390 に答える
7

あなたがほのめかしている問題は、Pythonでの変数の字句スコープと動的スコープの1つです。明確にするために、Pythonは次の4つのスコープを定義します。

  1. 最初に検索される最も内側のスコープには、ローカル名が含まれています
  2. 最も近い囲みスコープから検索される囲み関数のスコープには、非ローカル名だけでなく非グローバル名も含まれます
  3. 最後から2番目のスコープには、現在のモジュールのグローバル名が含まれます
  4. 最も外側のスコープ(最後に検索された)は、組み込みの名前を含む名前空間です

「y」が関数バーの外側で定義されている最初の例では、Pythonは最も内側のスコープを検索し、モジュールでグローバル変数「y」が見つかるまでチェーンを上に移動しました。

2番目の例では、「x」が関数foo(x)によって定義されており、xは、バーの内側に出力されるときに、囲んでいる関数のスコープに属します。純粋に、クロージャが定義されています。

Pythonでのスコープに関する詳細な調査については、次の記事を読んでください。

于 2010-11-27T15:25:11.300 に答える
1

どちらの例でも、ルックアップは実行時に行われます。唯一の違いは、ローカルに定義された変数xがあり、ローカルに定義された変数がないことですy

実行時...

def foo(x):
    def bar():
        print y

    return bar

y = 5
bar = foo(2)
bar()

...printステートメントは、という名前の変数をy検索し、グローバルコンテキストでのみ検出するため、その変数を使用して「5」を出力します。

の ...

def foo(x):
    def bar():
        print x

    return bar

x = 5
bar = foo(2)
bar()

...ルックアップが発生する、スコープ変数が定義されます。これは、関数が呼び出され xたときに「5」に固定されます。foo

重要なのは、引数が関数に渡されるときに評価されるため、外部関数fooは呼び出されたときに渡される引数を評価するということです。xこれにより、関数のコンテキストで呼び出される変数が効果的に作成されるfooため、実行するたびbarに、グローバルに定義された変数ではなく、その変数が表示されます。

次のコードのように、これは混乱を招くことがあります。

lst = []
for i in range(5):
    x = i
    lst.append(lambda: x)

for func in lst:
    print func()  # prints 4 4 4 4 4

あなたがする必要があります:

lst = []
for i in range(5):
    def _func(x):
        return lambda: x

    lst.append(_func(i))

for func in lst:
    print func()  # prints 0 1 2 3 4
于 2010-11-27T15:25:33.593 に答える
0

何も奇妙なことではありません。関数の引数の「x」がグローバル変数「x」よりも優先度が高いためです。

最初は、グローバル変数は大きな悪です。

Pythonには「グローバル」演算子があります。

>>> def foo(x):
...     def bar():
...          global x
...          print x
...     return bar
... 
>>> x = 5
>>> bar = foo(2)
>>> bar()
5
于 2010-11-27T15:18:17.873 に答える
0

これはスコープの母体です。2番目の例では、グローバルに宣言された上記の前にあるローカルスコープ変数xを使用します。

于 2010-11-27T15:18:59.000 に答える