[Python-ideas] 'Injecting' objects as function-local constants
Terry Reedy
tjreedy at udel.edu
Mon Jun 13 21:33:24 CEST 2011
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
> (<cell at 0xb7bd8a1c: int object at 0x81b20c0>,)
> >>> 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__
(<cell at 0x00FE0330: int object at 0x1E2139A0>,)
>>> funcs[9].__closure__
(<cell at 0x00FE0330: int object at 0x1E2139A0>,)
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__
(<cell at 0x00FCB4D0: int object at 0x1E213910>,)
>>> funcs[9].__closure__
(<cell at 0x00FE0750: int object at 0x1E2139A0>,)
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
More information about the Python-ideas
mailing list