Python lambda 闭包作用域[重复]

2025-02-05 13:23:00
admin
原创
53
摘要:问题描述:我正在尝试使用闭包从函数签名中消除一个变量(该应用程序是为了编写连接 Qt 信号所需的所有函数,以便为接口控制大量参数到存储值的字典中)。 我不明白为什么使用lambda未包装在另一个函数中的情况会返回所有情况的姓氏。names = ['a', 'b', 'c'] def test_fun(nam...

问题描述:

我正在尝试使用闭包从函数签名中消除一个变量(该应用程序是为了编写连接 Qt 信号所需的所有函数,以便为接口控制大量参数到存储值的字典中)。

我不明白为什么使用lambda未包装在另一个函数中的情况会返回所有情况的姓氏。

names = ['a', 'b', 'c']

def test_fun(name, x):
    print(name, x)

def gen_clousure(name):
    return lambda x: test_fun(name, x)

funcs1 = [gen_clousure(n) for n in names]
funcs2 = [lambda x: test_fun(n, x) for n in names]

# this is what I want
In [88]: for f in funcs1:
   ....:     f(1)
a 1
b 1
c 1

# I do not understand why I get this
In [89]: for f in funcs2:
   ....:     f(1)
c 1
c 1
c 1

解决方案 1:

原因是闭包(lambda 或其他)封闭的是名称,而不是值。定义时lambda x: test_fun(n, x),不会对 n 进行求值,因为它位于函数内部。调用函数时会对其进行求值此时的值是循环中的最后一个值。

您在开头说要“使用闭包从函数签名中消除变量”,但实际上并不是这样。(不过,请参见下文,了解一种可能让您满意的方法,具体取决于您所说的“消除”的含义。)函数定义时不会评估函数体内的变量。为了让函数在函数定义时获取变量的“快照”,您必须将变量作为参数传递。通常的方法是给函数一个参数,其默认值是来自外部范围的变量。看看这两个例子的区别:

>>> stuff = [lambda x: n+x for n in [1, 2, 3]]
>>> for f in stuff:
...     print f(1)
4
4
4
>>> stuff = [lambda x, n=n: n+x for n in [1, 2, 3]]
>>> for f in stuff:
...     print f(1)
2
3
4

在第二个示例中,将 nn作为参数传递给函数会将 n 的当前值“锁定”到该函数。如果您想以这种方式锁定该值,则必须执行类似这样的操作。(如果不这样做,全局变量之类的东西根本就不起作用;在使用时查找自由变量至关重要。)

请注意,此行为并非 lambda 所特有。如果您使用deflambda 定义引用封闭作用域中的变量的函数,则相同的作用域规则也有效。

如果您确实想要这样做,您可以避免向返回的函数添加额外的参数,但要做到这一点,您必须将该函数包装在另一个函数中,如下所示:

>>> def makeFunc(n):
...     return lambda x: x+n
>>> stuff = [makeFunc(n) for n in [1, 2, 3]]
>>> for f in stuff:
...     print f(1)
2
3
4

n这里,内部 lambda 仍然会在调用时查找 的值。但n它引用的 不再是全局变量,而是封闭函数 内的局部变量makeFunc。每次makeFunc调用 时都会创建一个这个局部变量的新值,并且返回的 lambda 会创建一个闭包,以“保存”对该 调用有效的局部变量值makeFunc。因此,循环中创建的每个函数都有自己的“私有”变量,称为x。(对于这个简单的例子,也可以使用外部函数的 lambda 来完成此操作 --- stuff = [(lambda n: lambda x: x+n)(n) for n in [1, 2, 3]]--- 但这种可读性较差。)

请注意,您仍然必须将 传递n为参数,只是,通过这种方式,您不会将其作为参数传递给最终进入列表的同一函数stuff;而是将其作为参数传递给创建要放入的函数的辅助函数stuff。使用这种双函数方法的优点是返回的函数是“干净的”并且没有额外的参数;如果您包装接受大量参数的函数,这可能会很有用,在这种情况下,记住参数n在列表中的位置可能会变得令人困惑。缺点是,这样做,创建函数的过程更加复杂,因为您需要另一个封闭函数。

结果是,存在一个权衡:您可以使函数创建过程更简单(即,不需要两个嵌套函数),但随后您必须使生成的函数更复杂一些(即,它有这个额外的n=n参数)。或者您可以使函数更简单(即,它没有n=n 个参数),但随后您必须使函数创建过程更复杂(即,您需要两个嵌套函数来实现该机制)。

相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   1565  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1354  
  信创国产芯片作为信息技术创新的核心领域,对于推动国家自主可控生态建设具有至关重要的意义。在全球科技竞争日益激烈的背景下,实现信息技术的自主可控,摆脱对国外技术的依赖,已成为保障国家信息安全和产业可持续发展的关键。国产芯片作为信创产业的基石,其发展水平直接影响着整个信创生态的构建与完善。通过不断提升国产芯片的技术实力、产...
国产信创系统   21  
  信创生态建设旨在实现信息技术领域的自主创新和安全可控,涵盖了从硬件到软件的全产业链。随着数字化转型的加速,信创生态建设的重要性日益凸显,它不仅关乎国家的信息安全,更是推动产业升级和经济高质量发展的关键力量。然而,在推进信创生态建设的过程中,面临着诸多复杂且严峻的挑战,需要深入剖析并寻找切实可行的解决方案。技术创新难题技...
信创操作系统   27  
  信创产业作为国家信息技术创新发展的重要领域,对于保障国家信息安全、推动产业升级具有关键意义。而国产芯片作为信创产业的核心基石,其研发进展备受关注。在信创国产芯片的研发征程中,面临着诸多复杂且艰巨的难点,这些难点犹如一道道关卡,阻碍着国产芯片的快速发展。然而,科研人员和相关企业并未退缩,积极探索并提出了一系列切实可行的解...
国产化替代产品目录   28  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用