python教程:关于 [lambda x: x*i for i in range(4)] 理解

部署运行你感兴趣的模型镜像

题目:

lst = [lambda x: x*i for i in range(4)]
res = [m(2) for m in lst]
print res

实际输出:[6, 6, 6, 6]

想要输出 [0, 2, 4, 6] 应该怎么改?如下:

lst = [lambda x, i=i: x*i for i in range(4)]
res = [m(2) for m in lst]
print res

这个问题涉及到了Python的闭包及延时绑定的知识(Python作用域)。

在Python核心编程里,闭包的定义如下:

如果在一个内部函数里,对外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认定是闭包。

总结为三点:

1、是一个内嵌函数

2、对外部函数变量引用

3、外部函数返回内嵌函数

简单的闭包例子:

def counter(start_at=0):
    count = [start_at]
    def incr():
        count[0] += 1
        return count[0]
    return incr

上面的那道题,可以写成这样:

'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:778463939
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
def func():
    fun_list = []
    for i in range(4):
        def foo(x):
            return x*i
        fun_list.append(foo)
    return fun_list
for m in func():
  print m(2)

func()是一个包含四个函数的列表:

[<function func at 0x00000000021CE9E8>, <function func at 0x00000000021CEA58>, <function func at 0x00000000021CEAC8>, <function func at 0x00000000021CEB38>]

当我们执行 m(2) 时,运行到foo()内部函数,发现变量 i 并不是foo()中的变量,于是就到外部函数func中寻找变量 i ,但此时外部的 for 已经循环完毕,最后的 i =3 。所以,每次

执行m(2),i 的值都是 3 ,因此,最终结果会是 [6, 6, 6, 6] 。

当在foo()中添加 i=i 后,即:

'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:778463939
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
def func():
    fun_list = []
    for i in range(4):
        def foo(x, i=i):
            return x*i
        fun_list.append(foo)
    return fun_list
for m in func():
  print m(2)

这样的话,for循环执行时,就已经把 i(0, 1, 2, 3) 的值传给了foo()函数,此时的 i 已经是foo()函数的内部变量,运行到foo()函数时,就不会到外部函数寻找变量 i ,直接运行

x*i(0, 1, 2, 3),因此最终结果会是 [0, 2, 4, 6] 。

您可能感兴趣的与本文相关的镜像

Python3.10

Python3.10

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

<think>好的,我现在要解决用户提出的关于Python的面试题:[lambda x: i*x for i in range(4)]。首先,我需要理解这个问题到底在问什么。看起来这是一个列表推导式,里面包含了四个lambda函数。用户可能想知道这段代码的输出是什么,或者其中可能存在的陷阱。 首先,我应该回忆一下列表推导式和lambda函数的基本知识。列表推导式会遍历range(4)中的每个i,分别为0,1,2,3,然后为每个i生成一个lambda函数。每个lambda函数的形式都是lambda x: i*x。这时候,我需要考虑变量i的作用域和闭包的问题。 接下来,可能的问题是,当这些lambda函数被创建后,调用它们时,i的值会是多少。因为lambda函数中的i是自由变量,它在运行时才会被解析。这时候,列表推导式中的循环变量i在循环结束后,其值会是最后一个值,也就是3。所以,所有四个lambda函数中的i都会引用这个最终的i值,也就是3。因此,当调用这些lambda函数时,比如f(2),每个函数都会返回3*2=6,而不是预期的0*2, 1*2, 2*2, 3*2。 不过,我需要验证这一点是否正确。比如,实际运行代码,看看结果是否四个函数都返回3*x。例如,执行以下代码: funcs = [lambda x: i*x for i in range(4)] print([f(2) for f in funcs]) 如果结果是[6,6,6,6],那么说明所有lambda函数都使用了i的最终值。这确实是一个常见的Python陷阱,涉及到闭包中变量的延迟绑定。 但为什么会发生这种情况呢?因为在Python中,闭包中的变量是在函数被调用时查找的,而不是在定义时。因此,当lambda函数被创建时,它们并没有捕获当前的i值,而是保留了一个对变量i的引用。当循环结束后,i的值变为3,所以所有函数调用都会使用这个值。 接下来,用户可能想知道如何解决这个问题,也就是让每个lambda函数捕获当前的i值。这时候,可以通过在lambda表达式中将i作为默认参数传递,或者使用立即绑定的方法。例如: funcs = [lambda x, i=i: i*x for i in range(4)] 这样,每个lambda函数在定义时就会捕获当前的i值作为默认参数,因此在调用时使用的i是定义时的值,而不是循环结束后的值。 另外,用户可能希望了解这种现象的原因以及如何避免。这可能涉及到Python的作用域规则和闭包机制。需要解释延迟绑定和早期绑定的区别,以及如何在列表推导式或循环中正确捕获变量。 此外,用户可能想知道这是否是Python的特定行为,还是其他语言也存在类似的问题。但根据当前的问题,可能只需要关注Python的情况。 总结来说,这个面试题考察的是对闭包和作用域的理解,特别是循环中变量绑定的常见陷阱。正确回答需要指出所有lambda函数共享同一个变量i,最终值为3,导致所有函数返回3*x。解决方法是在定义lambda时固定i的值,例如通过默认参数。</think>这是一个关于 Python**闭包(Closure)****变量作用域** 的经典面试题,涉及 `lambda` 表达式在循环中的行为。问题通常表现为以下形式: ```python funcs = [lambda x: i * x for i in range(4)] ``` 当调用 `funcs` 中的函数时(如 `f(2)`),预期输出可能是 `0, 2, 4, 6`,但实际结果会全部是 `6`。以下是详细解释: --- ### **1. 问题分析** #### **代码行为** - 这段代码通过列表推导式生成了 4 个 `lambda` 函数,每个函数接受参数 `x`,并返回 `i * x`。 - **预期**:每个 `lambda` 函数应捕获循环中的当前 `i` 值(0, 1, 2, 3),返回 `0*x`, `1*x`, `2*x`, `3*x`。 - **实际结果**:所有 `lambda` 函数都使用 `i` 的最终值 `3`,因此 `3*x`。 #### **示例验证** ```python funcs = [lambda x: i * x for i in range(4)] print([f(2) for f in funcs]) # 输出 [6, 6, 6, 6],而非预期的 [0, 2, 4, 6] ``` --- ### **2. 原因解释** - **闭包的延迟绑定**Python 中的 `lambda` 函数在定义时不会立即捕获变量 `i` 的值,而是**保留对变量 `i` 的引用**。当 `lambda` 函数被调用时,才会查找 `i` 的值。 - **循环结束后**: `i` 的值在循环结束时为 `3`,因此所有 `lambda` 函数最终都使用 `i=3`。 #### **等效代码** ```python i = 0 lambda x: i * x # 第一个函数 i = 1 lambda x: i * x # 第二个函数 ... i = 3 # 循环结束后,所有函数最终共享 i=3 ``` --- ### **3. 解决方案** 需要让每个 `lambda` 函数捕获循环中当前的 `i` 值,而不是共享最终的 `i`。以下是两种常见方法: #### **(1) 使用默认参数固化当前值** 通过将 `i` 作为默认参数传递给 `lambda`,可以在定义时立即捕获当前值: ```python funcs = [lambda x, i=i: i * x for i in range(4)] print([f(2) for f in funcs]) # 输出 [0, 2, 4, 6] ``` #### **(2) 通过闭包工厂函数** 显式创建闭包,隔离变量作用域: ```python def make_lambda(i): return lambda x: i * x funcs = [make_lambda(i) for i in range(4)] print([f(2) for f in funcs]) # 输出 [0, 2, 4, 6] ``` --- ### **4. 核心知识点** - **闭包的变量查找机制**:闭包中引用的外部变量是“延迟绑定”的,在函数调用时动态查找。 - **循环变量的作用域**:在循环中定义的变量(如 `i`)属于外部作用域,而非每次循环的独立作用域。 - **不可变类型 vs 可变类型**:若 `i` 是可变对象(如列表),修改其内容仍会影响所有闭包。 --- ### **5. 面试回答示例** > **面试官**:解释 `[lambda x: i*x for i in range(4)]` 的问题,如何解决? > **回答**: > 这段代码生成的每个 `lambda` 函数共享同一个变量 `i`。由于闭包的延迟绑定,函数在调用时才会查找 `i` 的值,而循环结束后 `i` 的最终值为 3,因此所有函数都会计算 `3*x`。 > 解决方法是让每个 `lambda` 在定义时捕获当前的 `i` 值,例如通过默认参数 `lambda x, i=i: i*x` 或使用闭包工厂函数隔离作用域。 --- 这个问题考察对 Python 作用域和闭包机制的理解,是面试中高频考点。
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值