zoukankan      html  css  js  c++  java
  • for循环中的lambda与闭包——Python的闭包是 迟绑定 , 这意味着闭包中用到的变量的值,是在内部函数被调用时查询

    for循环中的lambda与闭包

    问题引入

    z = [lambda x:x*i for i in range(3)]
    
    x = [o(2) for o in z]
    
    print x  # [4,4,4]
    

    f = [lambda x: x*i for i in range(3)](与x无关)

    i就是在闭包作用域(enclousing),而Python的闭包是 迟绑定 , 这意味着闭包中用到的变量的值,是在内部函数被调用时查询得到的

    调用结果

    >>> f = [lambda x:x*i for i in range(3)]
    >>> f
    [<function <listcomp>.<lambda> at 0x00000237F7DECD08>, <function <listcomp>.<lambda> at 0x00000237F7DECD90>, <function <listcomp>.<lambda> at 0x00000237F7DECE18>]
    >>> f[0](3)
    6  # 2 * 3
    >>> f[1](3)
    6  # 2 * 3
    >>> f[2](3)
    6  # 2 * 3
    
    >>> f[0](1)
    2  # 2 * 1
    >>> f[1](1)
    2  # 2 * 1
    >>> f[2](1)
    2  # 2 * 1
    

    当调用 func() 时,每循环一次,将 lam 函数的地址存到 fs 中。因为在每次循环中 lam函数都未绑定 i 的值,所以直到循环结束,i 的值为2,并将 lam 中所用到的 i 值定为 2 ,因此真正调用(例如f[0](2))的时候 i 值保持不变(为2),如下。

    表达式用普通函数表示为:

    def func():
        fs = []
        for i in range(3):
            print('进入lam前i的值:', i)
    
            def lam(x):
                print('进入lam后i的值:', i)
                return x * i
            fs.append(lam)
        return fs
    
    
    F = func()
    print(F)
    """
    进入lam前i的值: 0
    进入lam前i的值: 1
    进入lam前i的值: 2
    [<function func.<locals>.lam at 0x000002B4845ACAE8>, <function func.<locals>.lam at 0x000002B4845ACB70>, <function func.<locals>.lam at 0x000002B4845ACBF8>]
    """
    # 将x的值传入
    for f in F:
        print(f(2))
    """
    进入lam后i的值: 2
    4
    进入lam后i的值: 2
    4
    进入lam后i的值: 2
    4
    """
    

    f = [lambda :i*3 for i in range(3)](与x无关)

    另一种将x换成i,就与传入的x值无关了。(这里 lambda 后面什么参数都不跟)

    >>> f = [lambda :i*3 for i in range(3)]
    >>> f[0]()
    6
    >>> f[1]()
    6
    >>> f[2]()
    6
    

    普通函数表示如下:

    def func():
        fs = []
        for i in range(3):
            print('进入lam前i的值:', i)
    
            def lam():
                print('进入lam后i的值:', i)
                return i * 3
            fs.append(lam)
        return fs
    
    
    F = func()
    print(F)
    """
    进入lam前i的值: 0
    进入lam前i的值: 1
    进入lam前i的值: 2
    [<function func.<locals>.lam at 0x000001842141CAE8>, <function func.<locals>.lam at 0x000001842141CD90>, <function func.<locals>.lam at 0x000001842141CE18>]
    """
    
    for f in F:
        print(f())
    """
    进入lam后i的值: 2
    6
    进入lam后i的值: 2
    6
    进入lam后i的值: 2
    6
    """
    

    f = [lambda x, i=i:x*i for i in range(3)](与x有关)

    变闭包作用域为局部作用域。其中,i=i前面的i就是局部作用域。

    >>> f = [lambda x, i=i:x*i for i in range(3)]
    >>> f[0](3)
    0
    >>> f[1](3)
    3
    >>> f[2](3)
    6
    

    换种写法:

    def func():
        fs = []
        for i in range(3):
            def lam(x, i=i):
                return x * i
            fs.append(lam)
        return fs
    
    
    F = func()
    for f in F:
        print(f(3))
    """
    0
    3
    6
    """
    

    f = [lambda i=i: i*i for i in range(3)]

    >>> f = [lambda i=i: i*i for i in range(3)]
    >>> f
    [<function <listcomp>.<lambda> at 0x00000237F7DECB70>, <function <listcomp>.<lambda> at 0x00000237F7DECAE8>, <function <listcomp>.<lambda> at 0x00000237F7DECBF8>]
    >>> f[0]()
    0
    >>> f[1]()
    1
    >>> f[2]()
    4
    

    上面的表达式展开如下(为了更直观,替换了变量):

    def func():
        fs = []
        for i in range(3):
            print('进入lam前i的值:', i)
    
            def lam(x=i):
                print('进入lam后i的值:', i)
                return x * x
            fs.append(lam)
        return fs
    
    
    F = func()
    print(F)
    """
    进入lam前i的值: 0
    进入lam前i的值: 1
    进入lam前i的值: 2
    [<function func.<locals>.lam at 0x0000025C91F9CC80>, <function func.<locals>.lam at 0x0000025C91F9CAE8>, <function func.<locals>.lam at 0x0000025C91F9CB70>]
    """
    
    for f in F:
        print(f(8))
    """
    进入lam后i的值: 2
    0
    进入lam后i的值: 2
    1
    进入lam后i的值: 2
    4
    """
    

    当调用 func() 时,每循环一次,将 lam 函数的地址存到 fs 中。但是在每次循环中 lam函数都将 i 值绑定到了 x 上,所以直到循环结束,不同地址的 lam 函数的 x 值为都不一样,因此真正调用(例如 f[0]())的时候 x 值都为当时被绑定的值。

    但如果给 lam 函数传了参数,例如 f[0](8),那么所有的调用结果都为传参的平方。与上面解释并不冲突,只是将传的参数绑定到了 x 上。

    >>> f = [lambda i=i: i*i for i in range(3)]
    >>> f[0](8)
    64
    >>> f[1](8)
    64
    >>> f[2](8)
    64
    

    f = [lambda x=i: i*i for i in range(3)]

    和第二种好像,只是变了一个字符,那么结果就大不一样了。因为局部变量是x,i是闭包,会迟绑定。

    对于上面的表达式,调用结果:

    >>> f = [lambda x=i: i*i for i in range(3)]
    >>> f[0]()
    4
    >>> f[1]()
    4
    >>> f[2]()
    4
    >>> f[0](10)
    4
    >>> f[1](10)
    4
    >>> f[2](10)
    4
    

    传不传参数都不影响结果。展开后:

    def func():
        fs = []
        for i in range(3):
            def lam(x=i):
                return i * i
            fs.append(lam)
        return fs
    
    
    F = func()
    for f in F:
        print(f(), f(10))
    """
    4 4
    4 4
    4 4
    

    虽然 lam 函数将 i 的值绑定到了 x 上,但函数体中并未使用 x,所以直到循环结束,i 的值变为2,才会在调用时使用。其实同第一种情况是一样的。

  • 相关阅读:
    android 权限及原理
    通讯协议的相关知识(备忘)
    MongoDB安装以及java开发入门<二>
    Struts2架构图
    Lucene查询对象笔记_TermQuery(笔记)
    mongodb指南(翻译)(二十) developer zone 索引(四)地理信息索引(转载)
    项目结尾公共模块WebService封装
    Redhat 5.5下安装MongoDB
    wsimport生成客户端出现的异常
    Hibernate关于空间表查询时的的一个异常
  • 原文地址:https://www.cnblogs.com/bonelee/p/12566853.html
Copyright © 2011-2022 走看看