News for hackers. Stuff that matters.
Re:Understanding a Python Closure Oddity by nbadg on Monday June 17, 2024 @11:09AM (#40706370)
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

Re:Understanding a Python Closure Oddity by NwtnsMthd on Monday June 17, 2024 @01:43PM (#40708599)
Does this work for user defined classes (objects) as well?
Re:Understanding a Python Closure Oddity by Jtsummers on Monday June 17, 2024 @05:50PM (#40711476)
Yes, and by the same mechanism and for the same reason.
Re:Understanding a Python Closure Oddity by amluto on Monday June 17, 2024 @02:36PM (#40709344)
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.)

Re:Understanding a Python Closure Oddity by tzs on Monday June 17, 2024 @03:09PM (#40709755)
Another way to use function arguments to fix it is to put another level of function in:

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

      def make_closure(n):
        def closure():
          return n
        return closure

      yield make_closure(number)