理解Python闭包

10

我一直以为Python 2.7函数是指它们被定义的范围。考虑以下代码。为什么第二个输出不是"calculating: sin"?

有没有办法修改代码使其按预期工作?

import math

mymath = dict()

for fun in ["sin", "cos"]:
    def _impl(val):
        print "calculating: %s" % fun
        return getattr(math, fun)(val)
    mymath[fun] = _impl

# calculating: cos
print mymath["cos"](math.pi)

# calculating: cos <- why?
print mymath["sin"](math.pi)
3个回答

11

fun 的值在函数被调用时被评估。

在你提供的例子中,fun 是一个全局变量,在 for 循环运行后其值为 "cos"。

我认为您期望在创建函数时替换 fun 的值,但实际上并不是这样。函数在运行时会像应该的那样评估变量的值。

这与您定义函数的命名空间无关,而与运行函数的命名空间有关。

import math

mymath = dict()

for fun in ["sin", "cos"]:
    def _impl(val):
        print "calculating: %s" % fun
        return getattr(math, fun)(val)
    mymath[fun] = _impl


fun = 'tan'
# will print and calculate tan
print mymath["cos"](math.pi)

3

从这段代码(它可以按照您的意图工作)

my = {}

def makefun(fun):
  def _impl(x):
    print fun, x
  return _impl

for fun in ["cos", "sin"]:
  my[fun] = makefun(fun)

# will print 'cos'
my['cos'](1)
fun = 'tan'
# will print 'cos'
my['cos'](2)

似乎并不是函数定义的命名空间决定闭包的性质,而是使用变量的命名空间。 更多测试:

my = dict()

fun = ''

def makefun():
  global fun   #This line is switched on or off
  fun = 'sin'
  def _impl(x):
    print fun, x
  return _impl

test = makefun()

#gives sin 1
test(1)
fun = 'cos'
#gives sin 2 if line global fun is used
#gives cos 2 if line global fun is NOT used
test(2)

因此,正确的解释似乎是闭包保存了其参数的引用,而不是值。

0

我认为你正在试图让事情变得更加困难: 以下是使用闭包的方法:

import math

mymath = dict()


def funcmaker(fun):
    print "creating %s function" % fun
    def calculate(val):
        print "calculating: %s" % fun
        return getattr(math, fun)(val)
    return calculate

print funcmaker("sin")(math.pi)
print funcmaker("cos")(math.pi)

上述代码将给您以下结果:

creating sin function
calculating: sin
1.22464679915e-16
creating cos function
calculating: cos
-1.0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接