• Python 3 函数自由变量的大坑


    Python中函数是一个对象, 和整数,字符串等对象有很多相似之处,例如可以作为其他函数的参数或返回对象, Python中的函数还可以携带自由变量, 两者无疑极大增进了Python的表达力.

    但是Python函数自由变量的内部机制和列表解析或for循环结合使用时却暗藏杀机:

    复制代码
    #---CASE 1
    fs = map(lambda i:(lambda j: i*j),range(6))
    print([f(2) for f in fs])
    
    #---CASE 2
    fs = [lambda j:i*j for i in range(6)]
    print([f(2) for f in fs])
    
    #---CASE 3
    fs = []
    for i in range(6):
        fs.append(lambda j:i*j)
        if i==3:
            break
    print([f(2) for f in fs])
    
    #---CASE 4
    fs = [(lambda i:lambda j:i*j)(i) for i in range(6)]
    print([f(2) for f in fs])
    复制代码

    结果:

    [0, 2, 4, 6, 8, 10]
    [10, 10, 10, 10, 10, 10]
    [6, 6, 6, 6]
    [0, 2, 4, 6, 8, 10]

    可以通过下面这个简单的测试来分析Python函数在执行时是如何确定自由变量的值的:

    复制代码
    i = 1
    def f(j):
        return i*j
    print(f(2)) # ---> 2
    
    i = 2
    print(f(2)) # ---> 4
    
    def g():
        i = 3
        def f(j):
            return i*j
        return f
    f = g()
    print(f(2)) # ---> 6
    
    i = 100
    print(f(2)) # ---> 6
    复制代码

    可见,当 函数f在*定义时*, Python不会记录自由变量'i'对应什么对象, 只会告诉f, 你有一个自由变量, 它的名字叫 'i'.

    接着, 当函数f在*执行时*, Python告诉f:
    (1) 空间上: 你需要在你被*定义时*的外层namespace里面去查找i对应的对象, 假设这个namespace为X.

    (2) 时间上: 是在你*当前运行时*, X 里面的 i 对应的对象. 

    上面那个简单测试中的 i = 2 之后, f(2)随之也返回4也能反映了这一点.

    CASE 2和3 也是如此, fs里面每个函数对应的自由变量i在*定义时*都是循环变量i, 因此*执行时*都是对应循环结束或跳出时i所指对象.

    而 CASE 1和4为什么能如愿发生变化呢?  这是因为函数对应的自由变量i不再是循环变量i, 而是外层lambda函数*执行时*,循环变量i所指对象在其栈上的拷贝,  由于每次调用外层lambda时i所指对象都不相同, 因此每个函数的自由变量也会指向不同的对象.

    最后, 列表解析里面的作用域是一个全新的作用域,  而普通的for循环则有所不同. 例如:

    复制代码
    #---CASE 2
    fs = [lambda j:i*j for i in range(6)]
    print([f(2) for f in fs])
    i = 4
    print([f(2) for f in fs])
    
    #---CASE 3
    fs = []
    for i in range(6):
        fs.append(lambda j:i*j)
    print([f(2) for f in fs])
    i = 4
    print([f(2) for f in fs])
    复制代码

    结果是:

    [10, 10, 10, 10, 10, 10]
    [10, 10, 10, 10, 10, 10]
    [10, 10, 10, 10, 10, 10]
    [8, 8, 8, 8, 8, 8]
  • 相关阅读:
    xml ui
    xml ui
    xml ui
    debug
    centOS7 mini配置linux服务器(一)安装centOs7
    数据结构之__链表
    数据结构之__队列
    数据结构之__栈
    在树莓派上使用 SSD1306 OLED 屏幕
    git官方手册
  • 原文地址:https://www.cnblogs.com/ITdong-1/p/9941877.html
Copyright © 2020-2023  润新知