2017-07-09 4 views
-1

通常の変数を使用する最初の例のネストされた関数は、外側のスコープの変数を変更できないのに対し、2番目の例のリスト内の項目を使用する変数は変更できます。通常の変数を使用するネストされた関数は、外部スコープのnamesake変数を変更できないのに対し、リスト内の項目を使用するネストされた関数は変更できません。

私は言語デザインの観点から答えを探しています。

ありがとうございました。

例1:

def make_counter(): 
    count = 0 
    def counter(): 
     count += 1 
     return count 
    return counter 

例2:

def make_counter(): 
    count = [0] 
    def counter(): 
     count[0] += 1 
     return count[0] 
    return counter 

答えて

3

リストはありませんしながら、Pythonでの整数は、imutableているためです。したがって

、あなたがcount += 1を書くとき、あなたがcount[0] += 1を書くとき、これは、対照的に、これは

と同等(当時 counterに対してローカルである)新しい変数を作成します

count = count + 1 

に相当しています

value = count[0] 
value = value + 1 
count[0] = value 

ここでは、countは新しい値に再割り当てされておらず、内部関数の外側に作成された元のcountを(新しく作成された) )valueそれに。

2

Pythonのクロージャは、まさしくこのように動作します。

すべてのフリー変数(ローカル変数でも変数でもない変数)は、内部の__closure__プロパティに保存されます。

In [1394]: def make_counter(): 
     ...:  count = 0 
     ...:  def counter(): 
     ...:   nonlocal count 
     ...:   count += 1 
     ...:   return count 
     ...:  return counter 
     ...: 
In [1395]: f = make_counter() 

In [1396]: f 
Out[1396]: <function __main__.make_counter.<locals>.counter> 

In [1397]: f.__closure__ 
Out[1397]: (<cell at 0x10af4bd98: int object at 0x10024ac20>,) 

In [1399]: f.__closure__[0].cell_contents 
Out[1399]: 0 

ちょうどので、最初のケースでは、int sが不変である、ということが起こるので、あなたは、元のcountを変更されていません。

fが呼び出されると、内部関数にはローカル変数countを含む独自の環境フレームが作成され、__closure__とマージされます。

fを呼び出してみて、カウンタがインクリメントされます。後者の場合

In [1400]: f() 
Out[1400]: 1 

In [1401]: f.__closure__[0].cell_contents 
Out[1401]: 1 

、あなたは変更可能で、リスト内のエントリを変更しています。参照は__closure__プロパティにコピーされます。ところで

は、make_counterはグローバル関数であるので、空__closure__属性を有する:

In [1403]: print(make_counter.__closure__) 
None 

あなたの第二の例を、少し改変:

In [1404]: def make_counter(): 
     ...:  count = [0] 
     ...:  print(id(count)) 
     ...:  def counter(): 
     ...:   nonlocal count 
     ...:   count[0] += 1 
     ...:   return count[0] 
     ...:  return counter 
     ...: 

In [1405]: f = make_counter() 
4495587464 

In [1408]: id(f.__closure__[0].cell_contents) 
Out[1408]: 4495587464 

idであります同一。

関連する問題