On 6/13/2011 3:11 AM, Steven D'Aprano wrote:
Terry Reedy wrote:
Or use closures, which were partly designed to replace default arg use.
Default args are specifically used in at least one use-case where closures give the wrong result.
I meant an explicit user-defined closure with a separate cell for each function ...
funcs = [lambda x: x+i for i in range(10)] funcs[0].__closure__ # may be different in Python 2.x (
,) funcs[0](42) # should return 42+0 51 |
not this implicit one where each function uses the *same* cell referring to the same int object.
funcs[0].__closure__ (
,) funcs[9].__closure__ ( | ,) |
The fundamental problem with this code for funcs is that "lambda x: x+i" is a *constant* equivalent to "def _(x): return x+i". Executing either 10 times creates 10 duplicate functions. The hypnotic effect of 'lambda' is that some do not immediately see the equivalence.
The usual solution is to *not* use a closure:
funcs = [lambda x, i=i: x+i for i in range(10)] funcs[0].__closure__ is None True funcs[0](42) 42 funcs[9](42) 51
The explicit closure solution intended to replace "lambda x,i=i:x+i" is
def makef(j): return lambda x: x+j
funcs = [makef(i) for i in range(10)] list(funcs[_](42) for _ in range(10)) [42, 43, 44, 45, 46, 47, 48, 49, 50, 51] funcs[0].__closure__ (
,) funcs[9].__closure__ ( | ,) |
We now have difference cells containing different ints. To get different functions from multiple compilations of one body we need either different defaults for pseudo-parameters or different closure cells. The rationale for adding the latter was partly to be an alternative to the former. Once closure cells were made writable with 'nonlocal', they gained additional uses, or rather, replaced the awkward hack of using mutable 1-element lists as closure contents, with the one elements being the true desired content. -- Terry Jan Reedy