Python 嵌套函数变量作用域
我看过几乎所有关于这个话题的问题,但我的代码还是不行。
我觉得我对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变量作用域的理解有什么问题吗?
9 个回答
这里有一个例子,可以帮助理解大卫的回答。
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
在它之前的行消失了。但大卫引用的文本解释了原因:在静态分析过程中,解释器判断 b
在 inner
函数中被赋值,因此它被认为是 inner
的局部变量。在打印那一行时,它试图打印在这个局部范围内的 b
,但此时还没有给它赋值。
在Python 3中,你可以使用nonlocal
语句来访问那些既不是本地的也不是全局的变量。
nonlocal
语句的作用是让一个变量的定义绑定到最近的一个已经创建的变量上。下面有一些例子来说明这个概念:
def sum_list_items(_list):
total = 0
def do_the_sum(_list):
for i in _list:
total += i
do_the_sum(_list)
return total
sum_list_items([1, 2, 3])
上面的例子会出错,错误信息是:UnboundLocalError: local variable 'total' referenced before assignment
,意思是你在使用这个变量之前没有给它赋值。
如果使用nonlocal
,代码就能正常运行了:
def sum_list_items(_list):
total = 0
def do_the_sum(_list):
# Define the total variable as non-local, causing it to bind
# to the nearest non-global variable also called total.
nonlocal total
for i in _list:
total += i
do_the_sum(_list)
return total
sum_list_items([1, 2, 3])
那么“最近”到底是什么意思呢?这里有另一个例子:
def sum_list_items(_list):
total = 0
def do_the_sum(_list):
# The nonlocal total binds to this variable.
total = 0
def do_core_computations(_list):
# Define the total variable as non-local, causing it to bind
# to the nearest non-global variable also called total.
nonlocal total
for i in _list:
total += i
do_core_computations(_list)
do_the_sum(_list)
return total
sum_list_items([1, 2, 3])
在上面的例子中,total
会绑定到do_the_sum
函数内部定义的变量,而不是sum_list_items
函数外部定义的变量,所以代码会返回0
。需要注意的是,如果在do_the_sum
中将total
声明为nonlocal
,那么上面的例子就会按预期工作。
def sum_list_items(_list):
# The nonlocal total binds to this variable.
total = 0
def do_the_sum(_list):
def do_core_computations(_list):
# Define the total variable as non-local, causing it to bind
# to the nearest non-global variable also called total.
nonlocal total
for i in _list:
total += i
do_core_computations(_list)
do_the_sum(_list)
return total
sum_list_items([1, 2, 3])
在上面的例子中,nonlocal
的赋值会向上查找两层,才找到sum_list_items
函数内部的total
变量。
当我运行你的代码时,出现了这个错误:
UnboundLocalError: local variable '_total' referenced before assignment
这个问题是由这一行引起的:
_total += PRICE_RANGES[key][0]
关于作用域和命名空间的文档中提到:
Python有一个特别的特点——如果没有
global
语句生效,对变量的赋值总是进入最内层的作用域。赋值并不是复制数据——它只是将变量名与对象绑定在一起。
所以这行代码实际上是在说:
_total = _total + PRICE_RANGES[key][0]
它在recurse()
的命名空间中创建了_total
。因为_total
是新的且未被赋值,所以你不能在加法中使用它。