トピックに関する他のほとんどすべての質問を読みましたが、私のコードはまだ機能しません。
python変数スコープ。
ここに私のコードがあります:
_PRICE_RANGES = {
64:(25, 0.35),
32:(13, 0.40),
16:(7, 0.45),
8:(4, 0.5)
}
def get_order_total(quantity):
global PRICE_RANGES
_total = 0
_i = PRICE_RANGES.iterkeys()
def recurse(_i):
try:
key = _i.next()
if quantity % key != quantity:
_total += PRICE_RANGES[key][0]
return recurse(_i)
except StopIteration:
return (key, quantity % key)
res = recurse(_i)
_
そして、私は得る
「グローバル名「_total」が定義されていません」
問題が__total
_割り当てにあることは知っていますが、その理由はわかりません。 recurse()
は親関数の変数にアクセスするべきではありませんか?
誰かが私にpython変数スコープについて何が欠けているのか説明できますか?
コードを実行すると、次のエラーが表示されます。
_UnboundLocalError: local variable '_total' referenced before assignment
_
この問題はこのラインによって引き起こされます:
__total += PRICE_RANGES[key][0]
_
スコープと名前空間に関するドキュメント はこう言っています:
Python)の特別な癖は、–
global
ステートメントが有効でない場合–名前への割り当ては常に最も内側のスコープに入ることです。割り当てはデータをコピーせず、名前をオブジェクトにバインドするだけです。
だから、行は事実上言っているので:
__total = _total + PRICE_RANGES[key][0]
_
recurse()
の名前空間に__total
_を作成します。 __total
_は新しく、割り当てられていないため、追加で使用することはできません。
以下は、デビッドの答えの本質に迫るイラストです。
def outer():
a = 0
b = 1
def inner():
print a
print b
#b = 4
inner()
outer()
ステートメントb = 4
をコメント化すると、このコードは0 1
を出力します。
しかし、その行のコメントを解除すると、print b
行でエラーが発生します
UnboundLocalError: local variable 'b' referenced before assignment
b = 4
が存在すると、その前の行でb
が何らかの形で消えてしまうのは不思議なようです。しかし、Davidが引用するテキストはその理由を説明しています:静的解析中に、インタープリターはbがinner
に割り当てられているため、それがinner
のローカル変数であると判断します。印刷行は、割り当てられる前に、その内部スコープでb
を印刷しようとします。
Python 3では、 nonlocal
ステートメント を使用して、非ローカル、非グローバルスコープにアクセスできます。
特別なオブジェクト、マップ、または配列を宣言する代わりに、関数属性を使用することもできます。これにより、変数のスコープが本当に明確になります。
def sumsquares(x,y):
def addsquare(n):
sumsquares.total += n*n
sumsquares.total = 0
addsquare(x)
addsquare(y)
return sumsquares.total
もちろん、この属性は関数呼び出しではなく、関数(定義)に属します。したがって、スレッド化と再帰に注意する必要があります。
これはredmanのソリューションのバリエーションですが、変数のカプセル化に配列ではなく適切な名前空間を使用します。
def foo():
class local:
counter = 0
def bar():
print(local.counter)
local.counter += 1
bar()
bar()
bar()
foo()
foo()
この方法でクラスオブジェクトを使用するのがugいハックまたはpythonコミュニティでの適切なコーディング手法と見なされるかどうかはわかりませんが、python = 2.xおよび3.x(2.7.3および3.2.3でテスト済み)このソリューションの実行時の効率についても不明です。
おそらくあなたはあなたの質問の答えを得ているでしょう。しかし、私は通常これを回避する方法を示したかったのですが、それはリストを使用することです。たとえば、これを行いたい場合:
X=0
While X<20:
Do something. ..
X+=1
代わりにこれを行います:
X=[0]
While X<20:
Do something....
X[0]+=1
このように、Xは決してローカル変数ではありません
以前は@redmanのリストベースのアプローチを使用していましたが、読みやすさの点では最適ではありません。
外側ではなく内側の関数の属性を使用することを除いて、修正された@Hansのアプローチがあります。これは再帰とより互換性があり、おそらくマルチスレッドでもあります:
_def outer(recurse=2):
if 0 == recurse:
return
def inner():
inner.attribute += 1
inner.attribute = 0
inner()
inner()
outer(recurse-1)
inner()
print "inner.attribute =", inner.attribute
outer()
outer()
_
これは印刷します:
_inner.attribute = 3
inner.attribute = 3
inner.attribute = 3
inner.attribute = 3
_
私が_s/inner.attribute/outer.attribute/g
_の場合、次のようになります:
_outer.attribute = 3
outer.attribute = 4
outer.attribute = 3
outer.attribute = 4
_
したがって、実際には、それらを内部関数の属性にする方が良いようです。
また、読みやすさの観点からは賢明なようです。変数は概念的に内部関数に関連しており、この表記法は、変数が内部関数と外部関数のスコープ間で共有されることを読者に思い出させるためです。読みやすさのわずかな欠点は、def inner(): ...
の後にのみ_inner.attribute
_を構文的に設定できることです。
哲学的な観点から見ると、1つの答えは「名前空間に問題がある場合は、独自の名前空間を付けてください!」かもしれません。
独自のクラスで提供することで、問題をカプセル化できるだけでなく、テストを簡単にし、厄介なグローバルを排除し、さまざまなトップレベル関数間で変数をシャベルで動かす必要性を減らします(疑いなくget_order_total
)。
基本的な変更に焦点を当てるためにOPのコードを保存し、
class Order(object):
PRICE_RANGES = {
64:(25, 0.35),
32:(13, 0.40),
16:(7, 0.45),
8:(4, 0.5)
}
def __init__(self):
self._total = None
def get_order_total(self, quantity):
self._total = 0
_i = self.PRICE_RANGES.iterkeys()
def recurse(_i):
try:
key = _i.next()
if quantity % key != quantity:
self._total += self.PRICE_RANGES[key][0]
return recurse(_i)
except StopIteration:
return (key, quantity % key)
res = recurse(_i)
#order = Order()
#order.get_order_total(100)
PSとして、別の答えのリストのアイデアの変形であるハックが、おそらくより明確で、
def outer():
order = {'total': 0}
def inner():
order['total'] += 42
inner()
return order['total']
print outer()
>>> def get_order_total(quantity):
global PRICE_RANGES
total = 0
_i = PRICE_RANGES.iterkeys()
def recurse(_i):
print locals()
print globals()
try:
key = _i.next()
if quantity % key != quantity:
total += PRICE_RANGES[key][0]
return recurse(_i)
except StopIteration:
return (key, quantity % key)
print 'main function', locals(), globals()
res = recurse(_i)
>>> get_order_total(20)
main function {'total': 0, 'recurse': <function recurse at 0xb76baed4>, '_i': <dictionary-keyiterator object at 0xb6473e64>, 'quantity': 20} {'__builtins__': <module '__builtin__' (built-in)>, 'PRICE_RANGES': {64: (25, 0.34999999999999998), 32: (13, 0.40000000000000002), 16: (7, 0.45000000000000001), 8: (4, 0.5)}, '__package__': None, 's': <function s at 0xb646adf4>, 'get_order_total': <function get_order_total at 0xb646ae64>, '__name__': '__main__', '__doc__': None}
{'recurse': <function recurse at 0xb76baed4>, '_i': <dictionary-keyiterator object at 0xb6473e64>, 'quantity': 20}
{'__builtins__': <module '__builtin__' (built-in)>, 'PRICE_RANGES': {64: (25, 0.34999999999999998), 32: (13, 0.40000000000000002), 16: (7, 0.45000000000000001), 8: (4, 0.5)}, '__package__': None, 's': <function s at 0xb646adf4>, 'get_order_total': <function get_order_total at 0xb646ae64>, '__name__': '__main__', '__doc__': None}
{'recurse': <function recurse at 0xb76baed4>, '_i': <dictionary-keyiterator object at 0xb6473e64>, 'quantity': 20}
{'__builtins__': <module '__builtin__' (built-in)>, 'PRICE_RANGES': {64: (25, 0.34999999999999998), 32: (13, 0.40000000000000002), 16: (7, 0.45000000000000001), 8: (4, 0.5)}, '__package__': None, 's': <function s at 0xb646adf4>, 'get_order_total': <function get_order_total at 0xb646ae64>, '__name__': '__main__', '__doc__': None}
{'recurse': <function recurse at 0xb76baed4>, '_i': <dictionary-keyiterator object at 0xb6473e64>, 'quantity': 20}
{'__builtins__': <module '__builtin__' (built-in)>, 'PRICE_RANGES': {64: (25, 0.34999999999999998), 32: (13, 0.40000000000000002), 16: (7, 0.45000000000000001), 8: (4, 0.5)}, '__package__': None, 's': <function s at 0xb646adf4>, 'get_order_total': <function get_order_total at 0xb646ae64>, '__name__': '__main__', '__doc__': None}
Traceback (most recent call last):
File "<pyshell#32>", line 1, in <module>
get_order_total(20)
File "<pyshell#31>", line 18, in get_order_total
res = recurse(_i)
File "<pyshell#31>", line 13, in recurse
return recurse(_i)
File "<pyshell#31>", line 13, in recurse
return recurse(_i)
File "<pyshell#31>", line 12, in recurse
total += PRICE_RANGES[key][0]
UnboundLocalError: local variable 'total' referenced before assignment
>>>
ご覧のとおり、合計はメイン関数のローカルスコープにありますが、再帰のローカルスコープにはありません(明らかに)、グローバルスコープにもありません。'get_order_totalのローカルスコープでのみ定義されているため