我正在阅读黑客和画家,并对作者提到的问题感到困惑,以说明不同编程语言的强大功能。
问题是:
我们想要编写一个生成累加器的函数 - 一个带有数字 n 的函数,并返回一个带有另一个数字 i 并返回 n的函数增加 i 。 (那是增加,而不是加号。累加器必须累积。)
作者提到了几种使用不同编程语言的解决方案。例如,Common Lisp:
(defun foo (n)
(lambda (i) (incf n i)))
和JavaScript:
function foo(n) { return function (i) { return n += i } }
但是,当谈到Python时,以下代码不起作用:
def foo(n):
s = n
def bar(i):
s += i
return s
return bar
f = foo(0)
f(1) # UnboundLocalError: local variable 's' referenced before assignment
一个简单的修改就可以了:
def foo(n):
s = [n]
def bar(i):
s[0] += i
return s[0]
return bar
我是Python新手。为什么第一个解决方案不起作用而第二个解决方案不起作用?作者提到了词汇变量,但我仍然没有得到它。
答案 0 :(得分:5)
s += i
只是s = s + i
的糖。 *
这意味着您为变量s
分配了一个新值(而不是将其变异)。分配给变量时,Python假定它是函数的本地变量。但是,在分配之前,需要评估s + i
,但s
是本地的,仍然未分配 - >错误。
在第二种情况s[0] += i
中,您永远不会直接分配给s
,而只能访问s
中的某个项目。因此,Python可以清楚地看到它不是局部变量,而是在外部范围内寻找它。
最后,一个更好的选择(在Python 3中)是明确地告诉它s
不是局部变量:
def foo(n):
s = n
def bar(i):
nonlocal s
s += i
return s
return bar
(实际上不需要s
- 您只需在n
内使用bar
即可。)
* The situation is slightly more complex,但重要的问题是计算和分配是在两个单独的步骤中执行的。
答案 1 :(得分:1)
无限生成器是一种实现。您可以在生成器实例上调用__next__
以迭代地提取连续结果。
def incrementer(n, i):
while True:
n += i
yield n
g = incrementer(2, 5)
print(g.__next__()) # 7
print(g.__next__()) # 12
print(g.__next__()) # 17
如果您需要灵活的增量器,一种可能性是面向对象的方法:
class Inc(object):
def __init__(self, n=0):
self.n = n
def incrementer(self, i):
self.n += i
return self.n
g = Inc(2)
g.incrementer(5) # 7
g.incrementer(3) # 10
g.incrementer(7) # 17
答案 2 :(得分:0)
以下内容可行:
def foo(n):
s = n
def bar(i):
s_ = s + i
return s_
return bar
内部函数bar
看起来在其范围内本地查找s
,如果找不到它,它会在找到{{1}的封闭范围内查找一级}这是s
的局部变量。但是,如果您说foo
,则将s = s + 1
声明为s
范围内的新局部变量("赋值语句在本地范围内创建变量") ,这会导致错误,因为在向其添加内容之前,您尚未为bar
分配值(引用它)。
在另一个例子中,说s
是不同的,因为你没有在栏中声明一个新的局部变量,你可以访问s[0] = s[0] + 1
的第一个元素,它可以在条形的封闭范围内找到。
答案 3 :(得分:-1)
在Python中,如果我们使用一个变量并将其传递给一个函数,那么它将是Call by Value,无论你对变量做出什么改变都不会反映到原始变量。
但是当您使用列表而不是变量时,您对函数列表所做的更改将反映在函数外部的原始列表中,因此这称为按引用调用。
这就是第二个选项确实有效的原因,第一个选项没有。