Why do Python yield statements form a closure?

前端 未结 3 754
情书的邮戳
情书的邮戳 2021-02-01 17:52

I have two functions that return a list of functions. The functions take in a number x and add i to it. i is an integer increasing from 0-

3条回答
  •  执念已碎
    2021-02-01 18:24

    No, yielding has nothing to do with closures.

    Here is how to recognize closures in Python: a closure is

    1. a function

    2. in which an unqualified name lookup is performed

    3. no binding of the name exists in the function itself

    4. but a binding of the name exists in the local scope of a function whose definition surrounds the definition of the function in which the name is looked up.

    The reason for the difference in behaviour you observe is laziness, rather than anything to do with closures. Compare and contrast the following

    def lazy():
        return ( lambda x: x+i for i in range(10) )
    
    def immediate():
        return [ lambda x: x+i for i in range(10) ]
    
    def also_lazy():
        for i in range(10):
            yield lambda x:x+i
    
    not_lazy_any_more = list(also_lazy())
    
    print( [ f(10) for f in lazy()             ] ) # 10 -> 19
    print( [ f(10) for f in immediate()        ] ) # all 19
    print( [ f(10) for f in also_lazy()        ] ) # 10 -> 19
    print( [ f(10) for f in not_lazy_any_more  ] ) # all 19 
    

    Notice that the first and third examples give identical results, as do the second and the fourth. The first and third are lazy, the second and fourth are not.

    Note that all four examples provide a bunch of closures over the most recent binding of i, it's just that in the first an third case you evaluate the closures before rebinding i (even before you've created the next closure in the sequence), while in the second and fourth case, you first wait until i has been rebound to 9 (after you've created and collected all the closures you are going to make), and only then evaluate the closures.

提交回复
热议问题