Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

You can put an extra 'level' in, to make both options return the same 0,1...9

    def loop():
        for number in range(10):

            def outer(n):
                def inner():
                    return n

                return inner

            yield outer(number)
Is there a neater way?


Yes. The usual way to do this is to bind them as defaults to an argument, for example:

    def loop():
        for number in range(10):

            def func_w_closure(_num=number):
                return _num

            yield func_w_closure
This works because default arguments in python are evaluated exactly once, at function definition time. So it's a way of effectively copying the ``number`` out of the closure[1] and into the function definition.

[1] side note, closures in python are always late-binding, which is what causes the behavior in OP


I’ve never thought that leaking this type of implementation detail into the return value (and return type!) was a nice solution. I like the double closure better, and one can shorten it a bit with a lambda.

For those who prefer a functional style, functools.partial can also solve this problem.

(I use Python, and I like a lot of things about Python, but I don’t like its scoping rules at all, nor do I like the way that Python’s closures work. I would use a double lambda.)


Does this work for user defined classes (objects) as well?


Yes, and by the same mechanism and for the same reason.


I would have just done something like

``` def loop(): for number in range(10): fixed_number = number def inner(): return fixed_number yield inner ```


Correctly formatted (two spaces preceding each line, one blank line before the first code line, no extra lines needed between code lines):

  def loop():
    for number in range(10):
      fixed_number = number
      def inner():
        return fixed_number
      yield inner
The output:

  eagerly = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
  lazily  = [9, 9, 9, 9, 9, 9, 9, 9, 9, 9]
It doesn't work because of the way Python's variables are scoped. Your fixed_number variable is still shared across all instances of inner. Python doesn't have any sort of block scoping like you seem to think it has.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: