returns Python 中的累加器的函数
Function that returns an accumulator in Python
我正在阅读 Hackers and Painters 并且对作者提到的一个问题感到困惑,以说明不同编程语言的力量。
问题是:
We want to write a function that generates accumulators—a function that takes a number n, and returns a function that takes another number i and returns n incremented by i. (That’s incremented by, not plus. An accumulator has to accumulate.)
作者提到了几种不同编程语言的解决方案。例如,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 的新手。为什么第一个解决方案不起作用而第二个解决方案起作用?作者提到了词法变量,我还是没看懂
在Python中,如果我们使用一个变量并将其传递给一个函数,那么无论您对变量所做的任何更改,它都将按值调用,它不会反映到原始变量中。
但是当您使用列表而不是变量时,您在函数中对列表所做的更改会反映在函数外部的原始列表中,因此这称为引用调用。
这就是第二个选项有效而第一个选项无效的原因。
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
- 您可以简单地在 bar
中使用 n
。)
*The situation is slightly more complex,但重要的问题是计算和赋值是在两个独立的步骤中执行的。
无限生成器是一种实现。您可以在生成器实例上调用 __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
如果您需要一个灵活的增量器,一种可能是 object-oriented 方法:
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
我正在阅读 Hackers and Painters 并且对作者提到的一个问题感到困惑,以说明不同编程语言的力量。
问题是:
We want to write a function that generates accumulators—a function that takes a number n, and returns a function that takes another number i and returns n incremented by i. (That’s incremented by, not plus. An accumulator has to accumulate.)
作者提到了几种不同编程语言的解决方案。例如,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 的新手。为什么第一个解决方案不起作用而第二个解决方案起作用?作者提到了词法变量,我还是没看懂
在Python中,如果我们使用一个变量并将其传递给一个函数,那么无论您对变量所做的任何更改,它都将按值调用,它不会反映到原始变量中。
但是当您使用列表而不是变量时,您在函数中对列表所做的更改会反映在函数外部的原始列表中,因此这称为引用调用。
这就是第二个选项有效而第一个选项无效的原因。
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
- 您可以简单地在 bar
中使用 n
。)
*The situation is slightly more complex,但重要的问题是计算和赋值是在两个独立的步骤中执行的。
无限生成器是一种实现。您可以在生成器实例上调用 __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
如果您需要一个灵活的增量器,一种可能是 object-oriented 方法:
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