18

私はこのコードを持っています:

#!/usr/bin/env python

def get_match():
  cache=[]
  def match(v):
    if cache:
      return cache
    cache=[v]
    return cache
  return match
m = get_match()
m(1)

私がそれを実行すると、それは言う:

UnboundLocalError: local variable 'cache' referenced before assignment

しかし、私がこれを行う場合:

#!/usr/bin/env python

def get():
  y = 1
  def m(v):
    return y + v
  return m

a=get()
a(1)

実行されます。

リストに何かありますか?または私のコード編成が間違っていますか?

4

4 に答える 4

31

問題は、変数cacheが関数一致の範囲内にないことです。これは、2番目の例のように読みたいだけの場合は問題ありませんが、割り当てている場合は、Pythonがローカル変数として解釈します。Python 3を使用している場合は、nonlocalキーワードを使用してこの問題を解決できます。Python2の場合、残念ながら簡単な回避策はありません。

def f():
    v = 0

    def x():
        return v    #works because v is read from the outer scope

    def y():
        if v == 0:  #fails because the variable v is assigned to below
            v = 1

    #for python3:
    def z():
        nonlocal v  #tell python to search for v in the surrounding scope(s)
        if v == 0:
            v = 1   #works because you declared the variable as nonlocal

問題はグローバル変数でも同じですglobal。グローバル変数に割り当てるたびに使用する必要がありますが、読み取るためには使用しないでください。

その背後にある理由の簡単な説明:Pythonインタープリターは、すべての関数を型の特別なオブジェクトにコンパイルしますfunction。このコンパイル中に、関数が作成するすべてのローカル変数をチェックします(ガベージコレクションなど)。これらの変数名は関数オブジェクト内に保存されます。外部スコープ変数を「シャドウ」する(同じ名前の変数を作成する)ことは完全に合法であるため、global(またはnonlocalpython3で)明示的に宣言されていない変数はローカル変数と見なされます。

関数が実行されると、インタプリタは検出したすべての変数参照を検索する必要があります。コンパイル中に変数がローカルであることが判明した場合は、関数f_localsディクショナリで検索されます。まだ割り当てられていない場合は、発生した例外が発生します。変数が関数スコープで割り当てられておらず、したがってそのローカルの一部ではない場合、その変数は周囲のスコープで検索されます。変数がそこで見つからない場合、同様の例外が発生します。

于 2012-08-23T12:56:24.750 に答える
7

変数へのアクセスは、変数の割り当てとは異なります。

グローバル変数についても同様の状況があります。どの関数からでもアクセスできますが、ステートメントなしで割り当てようとするとglobal、ローカルコンテキストで再宣言されます。

残念ながら、ローカル関数の場合、globalステートメントに相当するものはありませんが、置き換えることで再宣言をバイパスできます。

cache=[v]

と:

cache[:] = [v]
于 2012-08-23T13:00:37.580 に答える
3

Pythonはcache=[v]-代入を参照するためcache、ローカル変数として扱います。したがって、エラーはかなり合理的です。ステートメントcacheで使用する前にローカル変数が定義されていません。if

あなたはおそらくそれを次のように書きたいでしょう:

def get_match():
  cache=[]
  def match(v):
    if cache:
      return cache
    cache.append(v)
    return cache
  return match
m = get_match()
m(1)

強く推奨される読み方:実行モデル-ネーミングとバインディングおよびPEP227-静的にネストされたスコープ

于 2012-08-23T12:57:57.380 に答える
1

交換

cache=[]
def match(v):

def match(v,cache=[])

説明:コードcacheはの変数として宣言しますがget_match、返されたものはmatch(v)何も知りません(次の割り当てのため)。ただし、の名前空間のcache一部になりたいと考えています。match

この方法で「悪意のある」ユーザーがキャッシュを再定義できることは知っていますが、それは彼ら自身の間違いです。ただし、これ問題になる場合、代替手段は次のとおりです。

def match(v):
     try:
         if cache:
             return cache
     except NameError:
         cache = []
     ...

ここを参照)

于 2012-08-23T13:03:37.880 に答える