【问题标题】:Binding an objects value within a function (closure)在函数内绑定对象值(闭包)
【发布时间】:2012-08-10 19:19:51
【问题描述】:

在 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 -- 这实际上是非常重要的。我一直使用这种东西将回调绑定到循环内的 GUI 小部件。
  • @tuxtimo 与 mcgilson 所说的原因几乎相同。我需要这样的东西来生成引用在for 循环中迭代的对象的函数列表。
  • 不同之处在于Python中的所有变量都是可变的(SMLrefs),所以x = 7更像是SML在ML中的x := 7而不是val x = 7。 (我猜第一个x = 3 就像val x = ref 3。)

标签: python closures sml


【解决方案1】:

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

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

(但是,如果您想要一个接受可变数量参数的函数,这将不起作用)。或者更一般地说(即使在变量参数的情况下也有效)但不太漂亮,使用立即执行的显式包装函数:

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

但是,我只是想指出 您的问题的前提是无效的 -- 在 SML 中,您永远不能分配 给变量(我我不是在谈论修改像ref 这样的可变数据结构;我是在谈论分配给变量);语言中甚至没有语法可以做到这一点。

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

【讨论】:

    【解决方案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 的值上。

    【讨论】:

    • 感谢您富有洞察力的回答。
    【解决方案3】:

    您可以使用关键字参数:

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

    关键字参数在创建函数时分配函数运行时会在函数的作用域中查找其他变量。 (如果在函数的作用域中找不到它们,python 会在包含函数的作用域中查找变量等)。

    【讨论】:

      【解决方案4】:

      你可以这样做:

         x = 3
         f = lambda y=x: y
         f()
         >>> 3
         x = 7
         f()
         >>> 3
      

      【讨论】:

        猜你喜欢
        • 2012-10-17
        • 2014-02-18
        • 1970-01-01
        • 1970-01-01
        • 2021-08-28
        • 1970-01-01
        • 2013-05-27
        • 2017-12-05
        • 1970-01-01
        相关资源
        最近更新 更多