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