以下闭包函数在javascript中工作正常。
function generateNextNumber(startNumber) {
var current = startNumber;
return function(){
return current += 1;
}
}
var getNextNumber = generateNextNumber(10);
for (var i = 0; i < 10; i++) {
console.log(getNextNumber());
}
我试图在Python中做同样的事情
def generateNextNumber(startNumber):
current = startNumber
def tempFunction():
current += 1
return current
return tempFunction
getNextNumber = generateNextNumber(10)
for i in range(10):
print (getNextNumber())
我收到以下错误
Traceback (most recent call last):
File "/home/thefourtheye/Desktop/Test1.py", line 10, in <module>
print (getNextNumber())
File "/home/thefourtheye/Desktop/Test1.py", line 4, in tempFunction
current += 1
UnboundLocalError: local variable 'current' referenced before assignment
当我在vars()
内打印locals()
和tempFunction
时,他们确认current
已存在。
({'current': 10}, {'current': 10})
但是当我修改程序时有点像这样
def generateNextNumber(startNumber):
current = {"Number" : startNumber}
def tempFunction():
current["Number"] += 1
return current["Number"]
return tempFunction
它有效。我无法解释为什么这样做。有人可以解释一下吗?
答案 0 :(得分:20)
Python假定函数中的所有变量都是本地的。这是为了避免意外使用同名的全局变量或封闭的范围。在某些重要的方面,这种差异是因为在Python中局部变量声明是自动/隐式的,而在JavaScript中则不是(你必须使用var
)。解决方案:
global
声明def generateNextNumber(startNumber):
global current
current= startNumber
def tempFunction():
global current
current += 1
return current
return tempFunction
在某些情况下有效,但在您的情况下,只有tempFunction
的一个实例可以同时处于活动状态。
def generateNextNumber(startNumber):
def tempFunction():
tempFunction.current += 1
return tempFunction.current
tempFunction.current= startNumber
return tempFunction
使用函数是对象(因此可以具有属性)的事实,它们在声明时被实例化,并且它们变为封闭函数(或模块,在这种情况下它们实际上是全局的)的本地。这也有效,因为名称tempFunction
首次在其自己的定义中使用“成员访问”.
运算符,因此不被假定为本地。 “call”()
和“element access”[]
运算符会发生类似情况。后一种情况解释了为什么你的代码有效。
def generateNextNumber(startNumber):
current= type("OnTheFly",(),{})()
current.value= startNumber
def tempFunction():
current.value += 1
return current.value
return tempFunction
这已在前一节中解释过。通过使用成员访问运算符.
,我们说“current
已经存在”,因此在封闭范围内搜索它。在这种特殊情况下,我们使用type
函数创建一个类,并立即创建它的实例(使用第二组parantheses)。我们也可以使用列表或字典而不是一般对象。第二种情况是一种非常普遍的解决方案。
def generateNextNumber(startNumber):
class TempFunction:
def __call__(self):
self.current += 1
return self.current
tempFunction= TempFunction()
tempFunction.current= startNumber
return tempFunction
任何类具有调用方法的对象是一个函数,因此可以使用函数调用运算符()
进行调用。这与之前的两个案例极为相关。
nonlocal
声明def generateNextNumber(startNumber):
current= startNumber
def tempFunction():
nonlocal current
current += 1
return current
return tempFunction
与global
的含义相同......好吧,全局,nonlocal
表示“在前一个范围内”。在Python 3和更高版本的Python 2中有效。
def generateNextNumber(current):
while True :
current+= 1
yield current
这可能是解决非局部变量访问的一般问题的最“Pythonic”方法,但是您用来解释它的具体情况。没有提到它我无法完成。不过,您需要稍加改动地调用它:
getNextNumber = generateNextNumber(10)
for i in range(10):
print (getNextNumber.next())
当驾驶for
时,对next()
的调用是隐含的(但生成器不能像我的示例中那样无限)。
答案 1 :(得分:3)
Python通过定义函数包含赋值为local的任何变量来决定函数的局部变量是什么,除非声明nonlocal
或global
。因此,
current += 1
创建一个名为current
的局部变量,隐藏非局部变量。如果您使用的是Python 2,那么标准解决方案(除了尝试不这样做之外)就是将current
作为1元素列表并使用
current[0] += 1
供参考,&#34;尽量不这样做&#34;可能看起来像下面这样:
class Counter(object):
def __init__(self):
self.count = 0
def __call__(self):
self.count += 1
return self.count
c = Counter()
c() # Returns 1
c() # Returns 2