can this be done without eval/exec?

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Schüle Daniel

    #1

    can this be done without eval/exec?

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

    and now the obvious one (as I thought at first)
    [color=blue][color=green][color=darkred]
    >>> lst=[]
    >>> for i in range(10):[/color][/color][/color]
    .... lst.append(lamb da:i)
    ....[color=blue][color=green][color=darkred]
    >>> lst[0]()[/color][/color][/color]
    9[color=blue][color=green][color=darkred]
    >>> i[/color][/color][/color]
    9[color=blue][color=green][color=darkred]
    >>>[/color][/color][/color]

    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
  • Chris Mellon

    #2
    Re: can this be done without eval/exec?

    On 4/26/06, Schüle Daniel <uval@rz.uni-karlsruhe.de> wrote:[color=blue]
    > Hello group,
    >[color=green][color=darkred]
    > >>> lst=[]
    > >>> for i in range(10):[/color][/color]
    > ... lst.append(eval ("lambda:%i" % i))
    > ...[color=green][color=darkred]
    > >>> lst[0]()[/color][/color]
    > 0[color=green][color=darkred]
    > >>> lst[1]()[/color][/color]
    > 1[color=green][color=darkred]
    > >>> lst[9]()[/color][/color]
    > 9[color=green][color=darkred]
    > >>>[/color][/color]
    >[color=green][color=darkred]
    > >>> lst=[]
    > >>> for i in range(10):[/color][/color]
    > ... exec "tmp = lambda:%i" % i # assignment is not expression
    > ... lst.append(tmp)
    > ...[color=green][color=darkred]
    > >>> lst[0]()[/color][/color]
    > 0[color=green][color=darkred]
    > >>> lst[1]()[/color][/color]
    > 1[color=green][color=darkred]
    > >>> lst[9]()[/color][/color]
    > 9[color=green][color=darkred]
    > >>>[/color][/color]
    >
    > and now the obvious one (as I thought at first)
    >[color=green][color=darkred]
    > >>> lst=[]
    > >>> for i in range(10):[/color][/color]
    > ... lst.append(lamb da:i)
    > ...[color=green][color=darkred]
    > >>> lst[0]()[/color][/color]
    > 9[color=green][color=darkred]
    > >>> i[/color][/color]
    > 9[color=green][color=darkred]
    > >>>[/color][/color]
    >
    > 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?
    >[/color]

    Using a factory function & closures instead of lambda:[color=blue][color=green][color=darkred]
    >>> def maker(x):[/color][/color][/color]
    .... def inner_maker():
    .... return x
    .... return inner_maker
    ....[color=blue][color=green][color=darkred]
    >>> lst = []
    >>> for i in range(10):[/color][/color][/color]
    .... lst.append(make r(i))
    ....[color=blue][color=green][color=darkred]
    >>> lst[0]()[/color][/color][/color]
    0[color=blue][color=green][color=darkred]
    >>> lst[5]()[/color][/color][/color]
    5[color=blue][color=green][color=darkred]
    >>> lst[9]()[/color][/color][/color]
    9[color=blue][color=green][color=darkred]
    >>>[/color][/color][/color]

    [color=blue]
    > Regards, Daniel
    > --
    > http://mail.python.org/mailman/listinfo/python-list
    >[/color]

    Comment

    • Schüle Daniel

      #3
      Re: can this be done without eval/exec?

      >> are there other solutions to this problem[color=blue][color=green]
      >> without use of eval or exec?
      >>[/color]
      >
      > Using a factory function & closures instead of lambda:[/color]
      [color=blue][color=green][color=darkred]
      >>> def a(x):[/color][/color][/color]
      .... def b():
      .... return x
      .... return b
      ....[color=blue][color=green][color=darkred]
      >>> lst=[]
      >>> for i in range(10):[/color][/color][/color]
      .... lst.append(a(i) )
      ....[color=blue][color=green][color=darkred]
      >>> lst[0]()[/color][/color][/color]
      0[color=blue][color=green][color=darkred]
      >>> lst[1]()[/color][/color][/color]
      1[color=blue][color=green][color=darkred]
      >>> lst[9]()[/color][/color][/color]
      9[color=blue][color=green][color=darkred]
      >>>[/color][/color][/color]

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

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

      Regards, Daniel

      Comment

      • Kent Johnson

        #4
        Re: can this be done without eval/exec?

        Schüle Daniel wrote:[color=blue]
        > and now the obvious one (as I thought at first)
        >[color=green][color=darkred]
        > >>> lst=[]
        > >>> for i in range(10):[/color][/color]
        > ... lst.append(lamb da:i)
        > ...[color=green][color=darkred]
        > >>> lst[0]()[/color][/color]
        > 9[color=green][color=darkred]
        > >>> i[/color][/color]
        > 9[color=green][color=darkred]
        > >>>[/color][/color]
        >
        > 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[/color]

        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.[color=blue]
        >
        > are there other solutions to this problem
        > without use of eval or exec?[/color]

        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(lamb da 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

        Comment

        • Schüle Daniel

          #5
          Re: can this be done without eval/exec?

          Kent Johnson schrieb:[color=blue]
          > Schüle Daniel wrote:[color=green]
          >> and now the obvious one (as I thought at first)
          >>[color=darkred]
          >> >>> lst=[]
          >> >>> for i in range(10):[/color]
          >> ... lst.append(lamb da:i)
          >> ...[color=darkred]
          >> >>> lst[0]()[/color]
          >> 9[color=darkred]
          >> >>> i[/color]
          >> 9[color=darkred]
          >> >>>[/color]
          >>
          >> 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[/color]
          >
          > 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.[color=green]
          >>
          >> are there other solutions to this problem
          >> without use of eval or exec?[/color]
          >
          > 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(lamb da 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[/color]

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

          Daniel

          Comment

          Working...