一文带你了解Python中的延迟绑定

 更新时间:2023年05月06日 09:34:28   作者:答案永恒  
Python中的延迟绑定是指在嵌套函数中,内部函数在被调用时才会绑定外部函数的变量,而不是在定义内部函数时就绑定。本文将通过一些例子带大家深入了解Python中的延迟绑定,感兴趣的可以了解一下

延迟绑定是什么?

Python中的延迟绑定是指在嵌套函数中,内部函数在被调用时才会绑定外部函数的变量,而不是在定义内部函数时就绑定。这种绑定方式可以导致一些出乎意料的行为,因为变量的值是在函数调用时决定的,而不是在函数定义时。

具体来说,当一个嵌套函数引用了外部函数的变量时,Python会在内部函数被调用时搜索变量的值,而不是在内部函数定义时。这意味着如果外部函数的变量在内部函数被调用之前被改变了,内部函数将使用新的变量值,而不是定义时的值。这种行为可能会导致一些困惑和错误,特别是在使用嵌套函数进行编程时。

举个栗子

下面是一个例子,展示了延迟绑定的行为:

def outer():
    numbers = [1, 2, 3, 4, 5]
    funcs = []
    for number in numbers:
        def inner():
            return number
        funcs.append(inner)
    return funcs
for func in outer():
    print(func())

输出结果为:

5
5
5
5
5

这是因为每个内部函数都引用了外部函数的 number 变量,但是这个变量在内部函数被调用时才会被绑定。由于 number 在每个迭代中的值都被重新赋值,所有内部函数都返回最后一个值,即 5。

为了避免延迟绑定可能导致的问题,可以通过将变量的值作为参数传递给内部函数来显式地绑定变量。例如,上面的代码可以修改如下:

def outer():
    numbers = [1, 2, 3, 4, 5]
    funcs = []
    for number in numbers:
        def inner(number=number):
            return number
        funcs.append(inner)
    return funcs
for func in outer():
    print(func())

输出结果为:

1
2
3
4
5

在这个版本中,每个内部函数都有一个默认参数 number,它的默认值是外部循环的 number 变量。由于默认参数的值在内部函数被定义时就被确定了,所以每个内部函数都绑定了不同的变量值。

另一个典型的栗子

def multipliers(): 
    return [lambda x : i*x for i in range(4)] 
print([m(2) for m in multipliers()])

输出结果为:

[6, 6, 6, 6]

是不是和你的想不一样呢!!为什么呢??

这是因为,在multipliers函数中,返回的是一个包含四个 lambda 函数的列表,这些 lambda 函数的形式参数为 x,函数体为 i*x。当这些 lambda 函数被调用时,它们的 i 取决于它们在列表中的索引,而不是在定义时的值。因此,当我们在 [m(2) for m in multipliers()] 中迭代这些 lambda 函数并传递 2 作为参数时,所有 lambda 函数的 i 都是最后一个 i 的值,即 3,因此所有的 lambda 函数都会返回 3*2=6

还不是很清楚?

没关系,让我们换一种方式解释下。

将 lambda 函数转换为等价的普通函数,可以更清晰地看到问题出在哪里。 首先,我们将原始的 lambda 函数:

lambda x : i*x

转换为等价的普通函数:

def multiplier(x):
    return i*x

然后,我们将 multipliers() 函数中的 lambda 函数列表转换为等价的普通函数列表:

def multipliers():
    funcs = []
    for i in range(4):
        def multiplier(x):
            return i*x
        funcs.append(multiplier)
    return funcs

现在,我们可以更清晰地看到问题出在哪里。在原始的 lambda 函数中,i 是一个自由变量,它的值在函数调用时动态绑定。但是,在 multipliers() 函数中,每个 multiplier() 函数都使用了同一个自由变量 i,其值在函数迭代结束后被设置为 3。因此,当我们迭代这些函数并传递 2 作为参数时,每个函数都会乘以最后一个 i 的值,也就是 3,所以结果会是 [6, 6, 6, 6]

如果要解决这个问题,可以使用闭包来捕获每个 lambda 函数所需的 i 值,使每个函数都有自己独立的 i 值。这样,当我们迭代这些函数并传递参数 2 时,每个函数都会乘以它们自己独立的 i 值,而不是最后一个 i 的值。

怎么避免这个问题呢

要避免这个问题,我们可以将 lambda 函数中的 i 变为默认参数,这样每个 lambda 函数都会有一个独立的 i 值。下面是一个修改后的代码:

def multipliers():
    return [lambda x, i=i : i*x for i in range(4)]
print([m(2) for m in multipliers()])

输出结果为:

[0, 2, 4, 6]

现在,每个 lambda 函数都有一个独立的 i 值,因此输出结果正确。

将 lambda 函数转换为等价的普通函数,可以更清晰地看到问题出在哪里。

def multipliers():
    funcs = []
    for i in range(4):
        def multiplier(x, i=i):
            return i*x
        funcs.append(multiplier)
    return funcs

这里我们使用了闭包来捕获每个 lambda 函数所需的 i 值,这样每个函数都有一个独立的 i 值。

现在,我们可以更清晰地看到问题出在哪里。在原始的 lambda 函数中,i 是一个自由变量,它的值在函数调用时动态绑定。但是,在 multipliers() 函数中,每个 multiplier() 函数都有自己独立的 i 值,这个值是在函数定义时静态绑定的。因此,当我们迭代这些函数并传递 2 作为参数时,所有函数的 i 值都是它们在定义时的值,而不是在调用时动态绑定的值。

通过使用闭包来捕获每个 lambda 函数所需的 i 值,我们可以解决这个问题,使每个函数都有自己独立的 i 值。

到此这篇关于一文带你了解Python中的延迟绑定的文章就介绍到这了,更多相关Python延迟绑定内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 利用Jmeter实现在请求param和body里面加入随机参数

    利用Jmeter实现在请求param和body里面加入随机参数

    本文介绍了如何使用jemeter实现新增接口压力测试中的随机参数生成,首先,使用函数助手对话框生成随机数,然后将生成的随机数作为参数放入请求中,无论请求格式是json、xml还是text等,如果param和body同时存在并需要随机生成参数,可以把参数写入到请求地址中
    2024-10-10
  • Python实现K-近邻算法的示例代码

    Python实现K-近邻算法的示例代码

    k-近邻算法(K-Nearest Neighbour algorithm),又称 KNN 算法,是数据挖掘技术中原理最简单的算法。本文将介绍实现K-近邻算法的示例代码,需要的可以参考一下
    2022-09-09
  • Python机器学习应用之基于BP神经网络的预测篇详解

    Python机器学习应用之基于BP神经网络的预测篇详解

    BP(back propagation)神经网络是1986年由Rumelhart和McClelland为首的科学家提出的概念,是一种按照误差逆向传播算法训练的多层前馈神经网络,是应用最广泛的神经网络模型之一
    2022-01-01
  • Numpy 理解ndarray对象的示例代码

    Numpy 理解ndarray对象的示例代码

    这篇文章主要介绍了Numpy 理解ndarray对象的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-04-04
  • 利用Python实现面部识别的方法详解

    利用Python实现面部识别的方法详解

    人脸识别正在成为软件开发中的一种趋势。它有助于识别人脸并使应用程序更加健壮。本文将使用python和face_recognition库创建一个简单的人脸识别,需要的可以参考一下
    2022-05-05
  • python 反编译exe文件为py文件的实例代码

    python 反编译exe文件为py文件的实例代码

    这篇文章主要介绍了python 反编译exe文件为py文件的实例代码,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下
    2019-06-06
  • Python phone模块获取手机号归属地 区号 运营商等信息demo

    Python phone模块获取手机号归属地 区号 运营商等信息demo

    这篇文章主要介绍了Python phone模块获取手机号归属地 区号 运营商等信息的实现示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-05-05
  • 利用Python实现Windows定时关机功能

    利用Python实现Windows定时关机功能

    是最初的几个爬虫,让我认识了Python这个新朋友,虽然才刚认识了几天,但感觉有种莫名的默契感。下面通过这篇文章给大家介绍Python实现Windows定时关机功能,需要的朋友可以参考下
    2017-03-03
  • 用Python实现斐波那契(Fibonacci)函数

    用Python实现斐波那契(Fibonacci)函数

    这篇文章主要介绍了用Python实现斐波那契(Fibonacci)函数的相关资料,需要的朋友可以参考下
    2016-03-03
  • python实现闹钟定时播放音乐功能

    python实现闹钟定时播放音乐功能

    这篇文章主要为大家详细介绍了python实现闹钟定时播放音乐功能,播放wav格式的音频,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-01-01

最新评论