free variables /cell objects question
Steven D'Aprano
steve at REMOVE.THIS.cybersource.com.au
Thu Jan 25 09:51:23 EST 2007
On Thu, 25 Jan 2007 04:29:35 -0800, Paul Rubin wrote:
> "gangesmaster" <tomerfiliba at gmail.com> writes:
>> what i see as a bug is this code not working as expected:
>>
>> >>> def make_foos(names):
>> ... funcs = []
>> ... for n in names:
>> ... def foo():
>> ... print "my name is", n
>> ... funcs.append(foo)
>> ... return funcs
>
> But it does work as expected, if your expectations are based on what
> closures actually do.
>
>> i have to create yet another closure, make_foo, so that the name
>> is correctly bound to the object, rather than the frame's slot:
>
> The Python idiom is:
>
> def make_foos(names):
> funcs = []
> for n in names:
> def foo(n=n):
> print "my name is", n
> funcs.append(foo)
> return funcs
>
> The n=n in the "def foo" creates the internal binding that you need.
Hmmm... I thought that the introduction of nested scopes removed the need
for that idiom. Its an ugly idiom, the less I see it the happier I am.
And I worry that it will bite you on the backside if your "n=n" is a
mutable value.
My solution is, don't try to have one function do too much. Making a list
of foos should be a separate operation from making a single foo:
>>> def makefoo(name):
... def foo():
... return "my name is " + name
... return foo
...
>>> makefoo("fred")()
'my name is fred'
>>> def makefoos(names):
... foos = []
... for name in names:
... foos.append(makefoo(name))
... return foos
...
>>> L = makefoos(["fred", "wilma"])
>>> L[0]()
'my name is fred'
>>> L[1]()
'my name is wilma'
That makes it easier to do unit testing too: you can test your makefoo
function independently of your makefoos function, if that's important.
If you absolutely have to have everything in one function:
>>> def makefoos(names):
... def makefoo(name):
... def foo():
... return "my name is " + name
... return foo
... L = []
... for name in names:
... L.append(makefoo(name))
... return L
...
>>> L = makefoos(["betty", "barney"])
>>> L[0]()
'my name is betty'
>>> L[1]()
'my name is barney'
Best of all, now I don't have to argue as to which binding behaviour is
more correct for closures!!! *wink*
--
Steven.
More information about the Python-list
mailing list