ここで、X は何らかのクロージャをサポートする任意のプログラミング言語 (C#、Javascript、Lisp、Perl、Ruby、Scheme など) です。
いくつかの制限はPython のクロージャ(Ruby のクロージャと比較して) に記載されていますが、この記事は古く、多くの制限は最新の Python には存在しません。
具体的な制限のコード例を見るのは素晴らしいことです。
関連する質問:
ここで、X は何らかのクロージャをサポートする任意のプログラミング言語 (C#、Javascript、Lisp、Perl、Ruby、Scheme など) です。
いくつかの制限はPython のクロージャ(Ruby のクロージャと比較して) に記載されていますが、この記事は古く、多くの制限は最新の Python には存在しません。
具体的な制限のコード例を見るのは素晴らしいことです。
関連する質問:
現在、最も重要な制限は、外部スコープの変数に代入できないことです。つまり、クロージャーは読み取り専用です。
>>> def outer(x):
... def inner_reads():
... # Will return outer's 'x'.
... return x
... def inner_writes(y):
... # Will assign to a local 'x', not the outer 'x'
... x = y
... def inner_error(y):
... # Will produce an error: 'x' is local because of the assignment,
... # but we use it before it is assigned to.
... tmp = x
... x = y
... return tmp
... return inner_reads, inner_writes, inner_error
...
>>> inner_reads, inner_writes, inner_error = outer(5)
>>> inner_reads()
5
>>> inner_writes(10)
>>> inner_reads()
5
>>> inner_error(10)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 11, in inner_error
UnboundLocalError: local variable 'x' referenced before assignment
ローカル スコープ (関数) で割り当てられる名前は、特に宣言されていない限り、常にローカルです。変数が代入されている場合でも変数をグローバルに宣言する「グローバル」宣言はありますが、囲まれた変数に対するそのような宣言はまだありません。Python 3.0 には、まさにそれを行う 'nonlocal' 宣言があります (存在する予定です)。
変更可能なコンテナー タイプを使用することで、当面の間、この制限を回避できます。
>>> def outer(x):
... x = [x]
... def inner_reads():
... # Will return outer's x's first (and only) element.
... return x[0]
... def inner_writes(y):
... # Will look up outer's x, then mutate it.
... x[0] = y
... def inner_error(y):
... # Will now work, because 'x' is not assigned to, just referenced.
... tmp = x[0]
... x[0] = y
... return tmp
... return inner_reads, inner_writes, inner_error
...
>>> inner_reads, inner_writes, inner_error = outer(5)
>>> inner_reads()
5
>>> inner_writes(10)
>>> inner_reads()
10
>>> inner_error(15)
10
>>> inner_reads()
15
特にPythonで遭遇する唯一の問題は、変数の再割り当てやクロージャなどの機能しない機能を組み合わせようとしたときに、これが機能しない場合に驚かされることです。
def outer ():
x = 1
def inner ():
print x
x = 2
return inner
outer () ()
通常、関数が独自のローカル変数を持っていることを指摘するだけで、そのような愚かさを阻止するのに十分です。
Javascript クロージャーと比較した Python クロージャーの制限 (または「制限」) は、効果的なデータ隠蔽に使用できないことです。
var mksecretmaker = function(){
var secrets = [];
var mksecret = function() {
secrets.push(Math.random())
}
return mksecret
}
var secretmaker = mksecretmaker();
secretmaker(); secretmaker()
// privately generated secret number list
// is practically inaccessible
import random
def mksecretmaker():
secrets = []
def mksecret():
secrets.append(random.random())
return mksecret
secretmaker = mksecretmaker()
secretmaker(); secretmaker()
# "secrets" are easily accessible,
# it's difficult to hide something in Python:
secretmaker.__closure__[0].cell_contents # -> e.g. [0.680752847190161, 0.9068475951742101]
nonlocal
次のステートメントを介して Python 3 で修正されました。
この
nonlocal
ステートメントにより、リストされた識別子は、グローバルを除く最も近い囲みスコープ内の以前にバインドされた変数を参照します。バインドの既定の動作では、最初にローカルの名前空間を検索するため、これは重要です。このステートメントにより、カプセル化されたコードは、グローバル (モジュール) スコープ以外のローカル スコープ外の変数を再バインドできます。
@ジョン・ミリキン
def outer():
x = 1 # local to `outer()`
def inner():
x = 2 # local to `inner()`
print(x)
x = 3
return x
def inner2():
nonlocal x
print(x) # local to `outer()`
x = 4 # change `x`, it is not local to `inner2()`
return x
x = 5 # local to `outer()`
return (inner, inner2)
for inner in outer():
print(inner())
# -> 2 3 5 4
コード例を含めるための@Kevin Littleの回答のコメント
nonlocal
python3.0 でこの問題を完全に解決するわけではありません:
x = 0 # global x
def outer():
x = 1 # local to `outer`
def inner():
global x
x = 2 # change global
print(x)
x = 3 # change global
return x
def inner2():
## nonlocal x # can't use `nonlocal` here
print(x) # prints global
## x = 4 # can't change `x` here
return x
x = 5
return (inner, inner2)
for inner in outer():
print(inner())
# -> 2 3 3 3
一方で:
x = 0
def outer():
x = 1 # local to `outer`
def inner():
## global x
x = 2
print(x) # local to `inner`
x = 3
return x
def inner2():
nonlocal x
print(x)
x = 4 # local to `outer`
return x
x = 5
return (inner, inner2)
for inner in outer():
print(inner())
# -> 2 3 5 4
それはpython3.1-3.3で動作します
3.0 までのより良い回避策は、囲まれた関数定義にデフォルトのパラメーターとして変数を含めることです。
デフ f() x = 5 デフ g(y, z, x=x): x = x + 1