can this be done without eval/exec?

  • Thread starter =?ISO-8859-1?Q?Sch=FCle_Daniel?=
  • Start date
?

=?ISO-8859-1?Q?Sch=FCle_Daniel?=

Hello group,
>>> lst=[]
>>> for i in range(10):
.... lst.append(eval("lambda:%i" % i))
....
>>> lst[0]() 0
>>> lst[1]() 1
>>> lst[9]() 9
>>>
>>> lst=[]
>>> for i in range(10):
.... exec "tmp = lambda:%i" % i # assignment is not expression
.... lst.append(tmp)
....
>>> lst[0]() 0
>>> lst[1]() 1
>>> lst[9]() 9
>>>

and now the obvious one (as I thought at first)
>>> lst=[]
>>> for i in range(10):
.... lst.append(lambda:i)
....

I think I understand where the problem comes from
lambda:i seems not to be fully evalutated
it just binds object with name i and not the value of i
thus lst[0]() is not 0

are there other solutions to this problem
without use of eval or exec?

Regards, Daniel
 
C

Chris Mellon

Hello group,
lst=[]
for i in range(10):
... lst.append(eval("lambda:%i" % i))
...
lst[0]() 0
lst[1]() 1
lst[9]() 9

lst=[]
for i in range(10):
... exec "tmp = lambda:%i" % i # assignment is not expression
... lst.append(tmp)
...
lst[0]() 0
lst[1]() 1
lst[9]() 9

and now the obvious one (as I thought at first)
lst=[]
for i in range(10):
... lst.append(lambda:i)
...

I think I understand where the problem comes from
lambda:i seems not to be fully evalutated
it just binds object with name i and not the value of i
thus lst[0]() is not 0

are there other solutions to this problem
without use of eval or exec?

Using a factory function & closures instead of lambda:.... def inner_maker():
.... return x
.... return inner_maker
....
.... lst.append(maker(i))
....
lst[0]() 0
lst[5]() 5
lst[9]() 9

Regards, Daniel
 
?

=?ISO-8859-1?Q?Sch=FCle_Daniel?=

are there other solutions to this problem
Using a factory function & closures instead of lambda:
.... def b():
.... return x
.... return b
....
>>> lst=[]
>>> for i in range(10):
.... lst.append(a(i))
....
>>> lst[0]() 0
>>> lst[1]() 1
>>> lst[9]() 9
>>>

yes this works
I was playing a little more with this idea
and got into the next trouble :)
.... def b():
.... return cnt
.... global cnt
.... cnt += 1
.... return b
....
>>> lst=[]
>>> for i in range(10):
.... lst.append(a())
....
>>> lst[0]() 10
>>> lst[1]() 10
>>>

I figured out what was wrong, here is corrected version
.... global cnt
.... tmp = cnt
.... def b():
.... return tmp
.... cnt += 1
.... return b
....
>>> lst=[]
>>> for i in range(10):
.... lst.append(a())
....
>>> lst[0]() 0
>>> lst[1]() 1
>>>

Regards, Daniel
 
K

Kent Johnson

Schüle Daniel said:
and now the obvious one (as I thought at first)
lst=[]
for i in range(10):
... lst.append(lambda:i)
...

I think I understand where the problem comes from
lambda:i seems not to be fully evalutated
it just binds object with name i and not the value of i
thus lst[0]() is not 0

The problem is that variables in closures are not bound until the
variable goes out of scope. So each lambda is bound to the final value of i.
are there other solutions to this problem
without use of eval or exec?

The workaround is to use a default argument to bind the current value of i:
In [1]: lst = []

In [2]: for i in range(10):
...: lst.append(lambda i=i: i)
...:
...:

In [3]: lst[0]()
Out[3]: 0

In [4]: lst[5]()
Out[4]: 5

A list comp makes this IMO cleaner:
In [5]: lst = [ lambda i=i: i for i in range(10) ]

In [6]: lst[0]()
Out[6]: 0

In [7]: lst[5]()
Out[7]: 5

Kent
 
?

=?ISO-8859-1?Q?Sch=FCle_Daniel?=

Kent said:
Schüle Daniel said:
and now the obvious one (as I thought at first)
lst=[]
for i in range(10):
... lst.append(lambda:i)
...

I think I understand where the problem comes from
lambda:i seems not to be fully evalutated
it just binds object with name i and not the value of i
thus lst[0]() is not 0

The problem is that variables in closures are not bound until the
variable goes out of scope. So each lambda is bound to the final value
of i.
are there other solutions to this problem
without use of eval or exec?

The workaround is to use a default argument to bind the current value of i:
In [1]: lst = []

In [2]: for i in range(10):
...: lst.append(lambda i=i: i)
...:
...:

In [3]: lst[0]()
Out[3]: 0

In [4]: lst[5]()
Out[4]: 5

A list comp makes this IMO cleaner:
In [5]: lst = [ lambda i=i: i for i in range(10) ]

In [6]: lst[0]()
Out[6]: 0

In [7]: lst[5]()
Out[7]: 5

Kent

many thanks for the explaination,
it look much simpler than my solutions too

Daniel
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,012
Latest member
RoxanneDzm

Latest Threads

Top