zoukankan      html  css  js  c++  java
  • Python中late-binding-closures

    2018-01-03 @望京

    示例1

    >>> b = []
    >>> for i in range(5):
    ...     b.append(lambda :i)
    ... 
    >>> for j in b:
    ...     print j()
    ... 
    4
    4
    4
    4
    4
    >>> for m in b:
    ...     print type(m),m
    ... 
    <type 'function'> <function <lambda> at 0x7fe4aae708c0>
    <type 'function'> <function <lambda> at 0x7fe4aae70938>
    <type 'function'> <function <lambda> at 0x7fe4aae709b0>
    <type 'function'> <function <lambda> at 0x7fe4aae70b18>
    <type 'function'> <function <lambda> at 0x7fe4aae70b90>
    >>> 
    >>> 

     为什么不是输出 0~4?   

    Closures in Python are late-binding, 
    meaning that each lambda function in the list will only evaluate the variable i when invoked, 
    and not when defined. 
    
    That's why all functions return the same value, i.e. the last value of ì (which is 4).
    

    late-binding-closures in Python  http://docs.python-guide.org/en/latest/writing/gotchas/#late-binding-closures

    怎么修改使之输出 0~4?   b.append(lambda i=i:i)

    >>> b = []
    >>> for i in range(5):
    ...     b.append(lambda i=i:i)
    ... 
    >>> for j in b:
    ...     print j()
    ... 
    0
    1
    2
    3
    4
    >>> 

    或者使用 functools.partial

    >>> b = []
    >>> for i in range(5):
    ...     from functools import partial
    ...     b.append(partial(lambda x:x, i))
    ... 
    >>> for j in b:
    ...     print j()
    ... 
    0
    1
    2
    3
    4
    >>> 

    参考:https://stackoverflow.com/questions/38369470/lambdas-from-a-list-comprehension-are-returning-a-lambda-when-called

    lambda补充

    >>> foo = [2, 18, 9, 22, 17, 24, 8, 12, 27]
    >>>
    >>> print filter(lambda x: x % 3 == 0, foo)   # 过滤
    [18, 9, 24, 12, 27]
    >>>
    >>> print map(lambda x: x * 2 + 10, foo)      # map
    [14, 46, 28, 54, 44, 58, 26, 34, 64]
    >>>
    >>> print reduce(lambda x, y: x + y, foo)     # 求和
    139
    >>>

    示例2

    >>> def func(arg=[]):
    ...     arg.append(1)
    ...     print arg
    ... 
    >>> func()
    [1]
    >>> func()
    [1, 1]
    >>> 

    修改1:在函数调用的时候传入参数

    >>> def func(arg=[]):
    ...     arg.append(1)
    ...     print arg
    ... 
    >>> func([])
    [1]
    >>> func([])
    [1]
    >>>  

    修改2:默认参数改为 None (更安全的做法)

    >>> 
    >>> def func(arg=None):
    ...     if not arg:
    ...         arg = []
    ...     arg.append(1)
    ...     print arg
    ... 
    >>> 
    >>> func()
    [1]
    >>> func()
    [1]
    >>>

    示例3

    >>> 
    >>> def func(x, l=[]):
    ...     for i in range(x):
    ...             l.append(i*i)
    ...     print l
    ... 
    >>> func(2)
    [0, 1]
    >>> func(3)
    [0, 1, 0, 1, 4]
    >>> 

    2018-03-12  https://zhuanlan.zhihu.com/p/33376761  

    今天在地铁上看到公众号推荐这个文章,也是late-binding问题,再补充下:

    def foo():
        temp = [lambda x : i*x for i in range(4)]
        return temp
    
    for bar in foo():
        print(bar(2))
    
    # 输出
    6
    6
    6
    6

    其实可以改写成下面这样,这样就比较容易看出是闭包了:lambda本身是一个函数,调用了外面的变量 i 

    temp = []
    for i in range(4):
        temp.append(lambda x : i*x)
    
    for bar in temp:
        print(bar(2))
    

    解决方法1:  [lambda x,i=i : i*x for i in range(4)]

    def foo():
        temp = [lambda x,i=i : i*x for i in range(4)]
        return temp
    
    for bar in foo():
        print(bar(2))

    解决方法2:使用 functools.partial

    from functools import partial
    from operator import mul
    
    def foo():
        temp = [partial(mul,i) for i in range(4)]
        return temp
    
    for bar in foo():
        print(bar(2))

    解决方法3:把temp改成生成器

    def foo():
        temp = (lambda x : i*x for i in range(4))
        return temp
    
    for bar in foo():
        print(bar(2))
    
    # 或者用 next() 方式调用
    # bar = foo()
    # print(next(bar)(2))
    # print(next(bar)(2))
    # print(next(bar)(2))
    # print(next(bar)(2))
    
    # 或者用 next() 方式调用
    # bar = foo()
    # print(bar.__next__()(2))
    # print(bar.__next__()(2))
    # print(bar.__next__()(2))
    # print(bar.__next__()(2))

    解决方法4:使用yield

    def foo():
        for i in range(4):
            yield lambda x : i*x
    
    for bar in foo():
        print(bar(2))

    补充

    构造生成器的两种方式:
        使用类似列表生成式的方式生成 (2*n + 1 for n in range(3, 11))
        使用包含yield的函数来生成
    
    如果计算过程比较简单,可以直接把列表生成式改成generator;
    但是,如果计算过程比较复杂,就只能通过包含yield的函数来构造generator。
    

      

    学无止境,戒骄戒躁。

    作者:Standby一生热爱名山大川、草原沙漠,还有妹子
    出处:http://www.cnblogs.com/standby/

    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

  • 相关阅读:
    开发细节
    html
    java学习
    Promise对象
    强制转换和隐式转换
    借助防抖解决输入框的非空校验
    setTimeout
    Symbol类型
    js API
    vue 使用mixin
  • 原文地址:https://www.cnblogs.com/standby/p/8260880.html
Copyright © 2011-2022 走看看