在函数(闭包)内绑定对象的值

3
在SML中(一种我在学Python之前学过的函数式编程语言),我可以做到以下几点:
val x = 3;
fun f() = x;
f();
>>> 3
val x = 7;
f();
>>> 3

然而在Python中,第一次调用会返回3,第二次调用会返回7。

x = 3
def f(): return x
f()
>>> 3
x = 7
f()
>>> 7

如何在Python中将变量的值绑定到函数上?


另一个问题,你为什么需要这个? - tuxtimo
1
@tuxtimo -- 这实际上非常重要。我经常在循环内部将回调绑定到GUI小部件这样的事情中使用它。 - mgilson
@tuxtimo 的需求和 mcgilson 提到的原因基本相同。我需要这样一个东西来生成一个函数列表,这些函数引用正在 for 循环中迭代的对象。 - Lanaru
不同之处在于Python中的所有变量都是可变的(SML ref),因此x = 7更像是ML中的x:= 7,而不是val x = 7。(我猜第一个x = 3就像val x = ref 3。) - Antal Spector-Zabusky
4个回答

7
您可以使用关键字参数:
x = 3
def f( x=x ): 
    return x

x = 7
f()  # 3

关键字参数在函数创建时被赋值。其他变量在函数运行时在函数的作用域中查找。(如果它们没有在函数的作用域中找到,Python 会在包含该函数的范围中查找该变量等等)。


2
你可以这样做:
   x = 3
   f = lambda y=x: y
   f()
   >>> 3
   x = 7
   f()
   >>> 3

2
作为mgilson和Neal所说的,最直接的方法是在定义f时使用默认参数。

更大的问题是这里的哲学不匹配。Python可以使用闭包,但它们的操作方式与您从SML(我不太熟悉)或Haskell或Clojure等语言中期望的方式不同。这是因为Python处理闭包的方式,以及它以不同的方式定义匿名函数并允许副作用。
通常,在函数式方法不起作用的情况下,您可以在Python中使用一个类来捕获x的值。
class F(object):
    def __init__(self, x):
        self.x = x
    def __call__(self):
        return self.x
x = 3
f = F(x)
f()
# 3
x = 7
f()
# 3

这个例子明显过于复杂了,我不建议在这里使用。但通常在Python中,答案是使用类。(这是一种夸张的说法,我的大脑比我打字还要快地生成异常。但当你从函数式编程背景转换时,我认为这个说法是一个很好的启发,让你学会用Python的方式来解决问题。
顺带一提,这也说明了在函数参数定义中捕获变量所隐含的含义:它创建了一个函数对象,在创建该对象时保留了x的值。

感谢您的深入回答。 - Lanaru

0

这个问题似乎是关于如何在创建闭包时捕获(而不是引用)外部变量的值。在这种意义上,通常的答案是使用默认参数:

def f(x=x): 
    return x
# or
f = lambda x=x: x

(然而,如果您想要一个接受可变数量参数的函数,则此方法将无法奏效)。或者更一般地说(即使在可变参数情况下也适用),但不太美观,可以使用立即执行的显式包装函数:

f = (lambda x: lambda: x)(x)

然而,我想指出的是,你问题的前提是无效的——在SML中,你永远不能对一个变量进行赋值(我不是在谈论修改可变数据结构,比如ref;我是在谈论对变量进行赋值);甚至语言中都没有这样的语法。

你在SML代码中所做的是创建一个新的作用域,并定义一个具有相同名称x不同变量,在新的作用域中隐藏了之前的x。你可以简单地将第二个变量重命名为不同的名称,对程序没有任何影响。现在,Python只有函数作用域,并且没有内部作用域用于函数内部的代码块。但是你可以应用相同的解决方案——只需将第二个变量重命名为不同的名称,就不会有这个问题。


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