回答:
内部関数は 2.xの非ローカル変数を読み取るますが、再バインドすることはできません。これは厄介ですが、回避できます。辞書を作成し、その中に要素としてデータを保存するだけです。内部関数から禁止されていない変異非ローカル変数が参照するオブジェクトを。
ウィキペディアの例を使用するには:
def outer():
d = {'y' : 0}
def inner():
d['y'] += 1
return d['y']
return inner
f = outer()
print(f(), f(), f()) #prints 1 2 3
def inner(): print d; d = {'y': 1}
ます。ここでは、print d
外部d
を読み取りd
、内部スコープで非ローカル変数を作成します。
X = 1
は名前X
を特定のオブジェクト(int
値を持つ1
)にバインドするだけです。 X = 1; Y = X
2つの名前をまったく同じオブジェクトにバインドします。とにかく、一部のオブジェクトは変更可能であり、その値を変更できます。
次の解決策はElias Zamariaの回答に触発されていますが、その回答に反して、外部関数の複数の呼び出しを正しく処理します。「変数」inner.y
はの現在の呼び出しに対してローカルですouter
。これは禁止されているため、変数ではありませんが、オブジェクト属性(オブジェクトは関数inner
そのもの)です。これは非常に見栄えが悪いですが(属性はinner
関数が定義された後でしか作成できないことに注意してください)、効果的なようです。
def outer():
def inner():
inner.y += 1
return inner.y
inner.y = 0
return inner
f = outer()
g = outer()
print(f(), f(), g(), f(), g()) #prints (1, 2, 1, 3, 2)
inc()
とdec()
共有カウンタをインクリメントとデクリメントすることを外から戻りました。次に、現在のカウンター値をアタッチする関数を決定し、他の関数からその関数を参照する必要があります。少し奇妙で非対称に見えます。例えばのdec()
ような行でinc.value -= 1
。
辞書ではなく、非ローカルクラスを整理します。@ChrisBの例を変更する:
def outer():
class context:
y = 0
def inner():
context.y += 1
return context.y
return inner
その後
f = outer()
assert f() == 1
assert f() == 2
assert f() == 3
assert f() == 4
それぞれのouter()呼び出しは、コンテキスト(新しいインスタンスだけでなく)と呼ばれる新しくて異なるクラスを作成します。そのため、共有コンテキストに関する@Nathanielの注意を回避できます。
g = outer()
assert g() == 1
assert g() == 2
assert f() == 5
__slots__ = ()
、クラスを使用する代わりにオブジェクトを追加して作成することで、たとえばcontext.z = 3
を発生させAttributeError
ます。スロットを定義していないクラスから継承しない限り、すべてのクラスで可能です。
ここで鍵となるのは、「アクセス」という意味です。クロージャスコープ外の変数の読み取りに問題はありません。たとえば、
x = 3
def outer():
def inner():
print x
inner()
outer()
期待どおりに動作するはずです(印刷3)。ただし、xの値のオーバーライドは機能しません。たとえば、
x = 3
def outer():
def inner():
x = 5
inner()
outer()
print x
3.は引き続き出力されます。PEP-3104についての私の理解から、これはnonlocalキーワードがカバーするものです。PEPで述べたように、クラスを使用して同じことを実行できます(厄介な種類)。
class Namespace(object): pass
ns = Namespace()
ns.x = 3
def outer():
def inner():
ns.x = 5
inner()
outer()
print ns.x
def ns(): pass
その後にを続けns.x = 3
ます。それはきれいではありませんが、私の目には少し醜くありません。
class Namespace: x = 3
ですか?
ns
これがグローバルオブジェクトなので、最後のステートメントのns.x
モジュールレベルで参照できます。print
。
Python 2で非ローカル変数を実装する別の方法があります。何らかの理由でここでの回答のいずれかが望ましくない場合です。
def outer():
outer.y = 0
def inner():
outer.y += 1
return outer.y
return inner
f = outer()
print(f(), f(), f()) #prints 1 2 3
変数の代入ステートメントで関数の名前を使用することは冗長ですが、変数を辞書に入れるよりも簡単でわかりやすく見えます。値は、Chris Bの回答と同じように、呼び出しごとに記憶されます。
f = outer()
、後で実行するとg = outer()
、f
のカウンターがリセットされます。これは、それぞれが独自の変数を持っているのではなく、両方が単一の outer.y
変数を共有しているためです。このコードは、Chris Bの答えよりも見た目が美しく見えますが、outer
複数回呼び出したい場合は、彼の方法が字句スコープをエミュレートする唯一の方法のようです。
outer.y
は、関数呼び出し(インスタンス)outer()
にローカルなものは何も含まれませんがouter
、それを含むスコープ内の名前にバインドされている関数オブジェクトの属性に割り当てられます。したがって、1も等しく書面で、使用している可能性がありouter.y
、他の代わりの名前をouter
、その範囲にバインドすることが知られている提供。これは正しいです?
outer.y
名前を使用する代わりにinner.y
(inner
呼び出し内にバインドされているためouter()
、まさに目的のスコープです)、innerの定義inner.y = 0
後の初期化(オブジェクトは、その属性が作成されるときに存在する必要があるため)ですが、もちろん?return inner
これは、アロイス・マダルが別の回答に関するコメントで行った提案に触発されたものです。
class Nonlocal(object):
""" Helper to implement nonlocal names in Python 2.x """
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
def outer():
nl = Nonlocal(y=0)
def inner():
nl.y += 1
return nl.y
return inner
f = outer()
print(f(), f(), f()) # -> (1 2 3)
更新
これを最近振り返った後、私はデコレータのようなものに感銘を受けました。それを1つに実装すると、より汎用的で使いやすくなるだろうと気づいたときです(ただし、そうすると、間違いなく読みやすさがある程度低下します)。
# Implemented as a decorator.
class Nonlocal(object):
""" Decorator class to help implement nonlocal names in Python 2.x """
def __init__(self, **kwargs):
self._vars = kwargs
def __call__(self, func):
for k, v in self._vars.items():
setattr(func, k, v)
return func
@Nonlocal(y=0)
def outer():
def inner():
outer.y += 1
return outer.y
return inner
f = outer()
print(f(), f(), f()) # -> (1 2 3)
どちらのバージョンもPython 2と3の両方で機能することに注意してください。
Pythonのスコープルールにはイボがあります。割り当てにより、変数はそのすぐ外側の関数スコープに対してローカルになります。グローバル変数の場合、これをglobal
キーワードで解決します。
解決策は、2つのスコープ間で共有されるオブジェクトを導入することです。これには、可変変数が含まれていますが、割り当てられていない変数を通じてそれ自体が参照されます。
def outer(v):
def inner(container = [v]):
container[0] += 1
return container[0]
return inner
別の方法は、スコープハッカーです。
def outer(v):
def inner(varname = 'v', scope = locals()):
scope[varname] += 1
return scope[varname]
return inner
パラメータの名前をに取得し、outer
それをvarnameとして渡すためのいくつかのトリックを理解できるouter
場合がありますが、名前に依存せずにYコンビネータを使用する必要があります。
nonlocal
。が定義されているときにローカルのlocals()
ディクショナリを作成しますが、そのディクショナリを変更してもin は変更されません。これは、closed over変数を共有したい内部関数が多い場合には機能しなくなります。a と言って、共有カウンターをインクリメントおよびデクリメントします。outer()
inner()
v
outer()
inc()
dec()
nonlocal
はpython 3の機能です。
nonlocal
Python 2 で Python 3の効果をどのように実現するかでした。あなたのアイデアは、一般的なケースではなく、1 つの内部関数を持つものだけをカバーしています。例として、この要点をご覧ください。両方の内部関数には独自のコンテナーがあります。他の回答がすでに示唆しているように、外側の関数のスコープに変更可能なオブジェクトが必要です。
nonlocal
のPython 3で導入されたキーワード
それを行う別の方法(冗長すぎます):
import ctypes
def outer():
y = 0
def inner():
ctypes.pythonapi.PyCell_Set(id(inner.func_closure[0]), id(y + 1))
return y
return inner
x = outer()
x()
>> 1
x()
>> 2
y = outer()
y()
>> 1
x()
>> 3
上記のMartineauのエレガントなソリューションを、実用的で少しエレガントではない使用例に拡張すると、次のようになります。
class nonlocals(object):
""" Helper to implement nonlocal names in Python 2.x.
Usage example:
def outer():
nl = nonlocals( n=0, m=1 )
def inner():
nl.n += 1
inner() # will increment nl.n
or...
sums = nonlocals( { k:v for k,v in locals().iteritems() if k.startswith('tot_') } )
"""
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
def __init__(self, a_dict):
self.__dict__.update(a_dict)
グローバル変数を使用する
def outer():
global y # import1
y = 0
def inner():
global y # import2 - requires import1
y += 1
return y
return inner
f = outer()
print(f(), f(), f()) #prints 1 2 3
個人的には、グローバル変数は好きではありません。しかし、私の提案はhttps://stackoverflow.com/a/19877437/1083704の回答に基づいています
def report():
class Rank:
def __init__(self):
report.ranks += 1
rank = Rank()
report.ranks = 0
report()
ranks
を呼び出す必要があるたびに、ユーザーがグローバル変数を宣言する必要がある場合report
。私の改善により、ユーザーから関数変数を初期化する必要がなくなりました。
inner
、割り当てることはできませんが、キーと値を変更できます。これにより、グローバル変数の使用が回避されます。