当前位置: 首页 > 面试题库 >

Python Lambda闭合作用域

刘胜泫
2023-03-14
问题内容

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

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

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

问题答案:

原因是闭包(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

在第二个示例中,将n参数作为参数传递给“锁定”该函数的当前n值。如果要以这种方式锁定值,则必须执行类似的操作。(如果它不能以这种方式工作,那么全局变量之类的东西将根本无法工作;在使用时查找自由变量是至关重要的。)

请注意,关于此行为的所有内容都不特定于lambda。如果def用于定义引用来自封闭范围的变量的函数,则相同的作用域规则有效。

如果确实需要,可以避免在返回的函数中添加额外的参数,但是,你必须将该函数包装在另一个函数中,如下所示:

>>> 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

在此,内部lambda仍会n在调用时查找其值。但是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参数),但是必须使函数创建过程更复杂(即,需要两个嵌套函数来实现该机制)。



 类似资料:
  • 前言 面试问题: 说一下对变量提升的理解 说明this的几种不同的使用场景 创建10个<a>标签,点击的时候弹出来对应的序号 如何理解作用域 实际开发中闭包的应用 涉及到的知识点: 执行上下文 this 作用域 作用域链 闭包 执行上下文 执行上下文主要有两种情况: 全局代码: 一段<script>标签里,有一个全局的执行上下文。所做的事情是:变量定义、函数声明 函数代码:每个函数里有一个上下文。

  • 作用域和闭包是 JavaScript 最重要的概念之一,想要进一步学习 JavaScript,就必须理解 JavaScript 作用域和闭包的工作原理。 作用域 任何程序设计语言都有作用域的概念,简单的说,作用域就是变量与函数的可访问范围,即作用域控制着变量与函数的可见性和生命周期。在 JavaScript 中,变量的作用域有全局作用域和局部作用域两种。 全局作用域(Global Scope) 在

  • JavaScript 是一种非常面向函数的语言。它给了我们很大的自由度。在 JavaScript 中,我们可以随时创建函数,可以将函数作为参数传递给另一个函数,并在完全不同的代码位置进行调用。 我们已经知道函数可以访问其外部的变量。 但是,如果在函数被创建之后,外部变量发生了变化会怎样?函数会获得新值还是旧值? 如果将函数作为参数传递并在代码中的另一个位置调用它,该函数将访问的是新位置的外部变量吗

  • 使用https://github.com/venomous0x/whatsapi通过Whats应用程序发送消息是否合法? 我想使用应用程序发送提醒和通知。

  • 本文向大家介绍javascript从作用域链谈闭包,包括了javascript从作用域链谈闭包的使用技巧和注意事项,需要的朋友参考一下 神马是闭包 关于闭包的概念,是婆说婆有理。 闭包是指有权访问另外一个函数作用域中的变量的函数 这概念有点绕,拆分一下。从概念上说,闭包有两个特点: 1、函数 2、能访问另外一个函数作用域中的变量 在ES 6之前,Javascript只有函数作用域的概念,没有块级作

  • 目标 无具体目标 知识点 理解 js 中 var 的作用域 了解闭包的概念 理解 this 的指向 课程内容 es6中新增了 let 关键词,与块级作用域,相关知识参考: http://es6.ruanyifeng.com/ var 作用域 先来看个简单的例子: var parent = function () { var name = "parent_name"; var age = 1