eval's local arguement ignored?

Discussion in 'Python' started by =?ISO-8859-1?Q?Jean-S=E9bastien_Bolduc?=, Sep 23, 2003.

  1. Hello,

    I would like to associate a local namespace with a lambda function. To
    be more specific, here is exactly what I would want:

    def foo():
    a = 1
    f = lambda x : a*x
    return f

    then if "f1 = foo()", I get a function whose parameter "a" is not in
    the global scope. But my functions are not to be hard-coded. I naively
    expected the following to work:

    def foo(funcstring):
    a = 1
    f = eval(funcstring)
    return f

    but "f2 = foo( 'lambda x : a*x' )" won't work: "f2()" will look for
    "a" in the globals.

    Looking at the documentation (I did!), I was then certain replacing
    the evaluation with the following would work:

    f = eval(funcstring, globals(), locals())

    Or, while we're at it, evaluating the whole thing outside of function
    "foo":

    f = eval('lambda x : a*x', globals(), {'a' : 1})

    Ooops! the new function still looks for "a" in the globals... Although
    I could not find any documentation about the fact, it seems the
    "local" argument to "eval" has no effect in the above cases!

    So for now, I'm doing something like

    f = eval('lambda x : a*x', {'a' : 1})

    but then my function looses access to the globals.

    I know that only the very first case above gives me a function whose
    "fun_closure" attribute is not "None". But still I cannot see where I
    go wrong.

    Thank you,

    JSeb
     
    =?ISO-8859-1?Q?Jean-S=E9bastien_Bolduc?=, Sep 23, 2003
    #1
    1. Advertising

  2. =?ISO-8859-1?Q?Jean-S=E9bastien_Bolduc?=

    Peter Abel Guest

    (Jean-S├ębastien Bolduc) wrote in message news:<>...
    > Hello,
    >
    > I would like to associate a local namespace with a lambda function. To
    > be more specific, here is exactly what I would want:
    >
    > def foo():
    > a = 1
    > f = lambda x : a*x
    > return f
    >
    > then if "f1 = foo()", I get a function whose parameter "a" is not in
    > the global scope. But my functions are not to be hard-coded. I naively
    > expected the following to work:
    >
    > def foo(funcstring):
    > a = 1
    > f = eval(funcstring)
    > return f
    >
    > but "f2 = foo( 'lambda x : a*x' )" won't work: "f2()" will look for
    > "a" in the globals.
    >
    > Looking at the documentation (I did!), I was then certain replacing
    > the evaluation with the following would work:
    >
    > f = eval(funcstring, globals(), locals())
    >
    > Or, while we're at it, evaluating the whole thing outside of function
    > "foo":
    >
    > f = eval('lambda x : a*x', globals(), {'a' : 1})
    >
    > Ooops! the new function still looks for "a" in the globals... Although
    > I could not find any documentation about the fact, it seems the
    > "local" argument to "eval" has no effect in the above cases!
    >
    > So for now, I'm doing something like
    >
    > f = eval('lambda x : a*x', {'a' : 1})
    >
    > but then my function looses access to the globals.
    >
    > I know that only the very first case above gives me a function whose
    > "fun_closure" attribute is not "None". But still I cannot see where I
    > go wrong.
    >
    > Thank you,
    >
    > JSeb


    **kwa** is the solution!

    In your factory-function *foo* you have to declare *a*
    keywordarguement which is initialized with a.

    >>> def foo():

    .... a=1
    .... f=lambda x,a=a:a*x
    .... return f

    >>> f1=foo()
    >>> f1(3)

    3

    A global *a* doesn't affect your function.
    >>> a=1000
    >>> f1(3)

    3

    but you even can get influence to *a*:
    >>> f1(3,a=100)

    300
    >>>

    or
    >>> f1(3,100)

    300
    >>>


    And this works too, even if you generate a lambda-function
    as adhoc-function in the global scope:
    !! You have to define *a* as first !!
    >>> a=100
    >>> f2=lambda x,a=a:a*x
    >>> f2(3)

    300

    To change the global *a* doesn't affect your lambda-function!
    >>> a=0
    >>> f2(3)

    300

    But you can give your function the additional information.
    >>> f2(3,a=1)

    3

    or more traditional
    >>> f2(3,1)

    3

    And the default kwa will stay resident.
    >>> f2(3)

    300
    >>>


    Regards
    Peter
     
    Peter Abel, Sep 26, 2003
    #2
    1. Advertising

  3. > **kwa** is the solution!
    >
    > In your factory-function *foo* you have to declare *a*
    > keywordarguement which is initialized with a.
    >
    > >>> def foo():

    > ... a=1
    > ... f=lambda x,a=a:a*x
    > ... return f


    Not exactly what I'm looking for, I'm afraid. A more complete picture
    is this: I'm writing a class such as this:

    class Foo:
    def __init__(self, fnc, **params):
    ...
    def evaluate(self, val)
    ...

    That has to be instantiated as, e.g.:

    x = Foo( 'lambda x : a*x', dict( a = 2. ) )

    so that "x.evaluate( 5. )" will return, in this case, "2.*5.".

    The approach you describe will certainly work, but the thing is that
    this class will have to be used by people who don't necessarily know
    Python (yet). So I would really like the lambda function's parameter
    list to always be the same, whatever you put on its RHS.

    Once again, I don't understand why the "eval", as described above with
    "globals" and "locals" arguments specified, will not work. Is there a
    way to modify a function's closure?

    Thanks,
    JSeb
     
    Jean-S?bastien Bolduc, Sep 30, 2003
    #3
  4. Jean-S?bastien Bolduc wrote:
    ...
    > is this: I'm writing a class such as this:
    >
    > class Foo:
    > def __init__(self, fnc, **params):
    > ...
    > def evaluate(self, val)
    > ...
    >
    > That has to be instantiated as, e.g.:
    >
    > x = Foo( 'lambda x : a*x', dict( a = 2. ) )


    Hmmm. 'a' is NOT a local in that lambda -- it's a global. locals
    are [a] arguments, plus variables that get re-bound within the
    function (which, in a lambda, means only control variables used in
    list comprehensions), PERIOD. That, if you want, is the DEFINITION
    of "local variable" in Python.

    So, that dict had better be in the GLOBALS, or else variable 'a' will
    of course not be fond.


    > so that "x.evaluate( 5. )" will return, in this case, "2.*5.".
    >
    > The approach you describe will certainly work, but the thing is that
    > this class will have to be used by people who don't necessarily know
    > Python (yet). So I would really like the lambda function's parameter
    > list to always be the same, whatever you put on its RHS.
    >
    > Once again, I don't understand why the "eval", as described above with
    > "globals" and "locals" arguments specified, will not work. Is there a
    > way to modify a function's closure?


    You may play with closures, yes (not really modifying a function's
    closure but rather building a new function object with a code taken
    from one place and a closure from another, say), but I'm not sure it
    would help you.

    Consider the following...:

    >>> import dis
    >>> thefunc = lambda x: a * x
    >>> dis.dis(thefunc)

    1 0 LOAD_GLOBAL 0 (a)
    3 LOAD_FAST 0 (x)
    6 BINARY_MULTIPLY
    7 RETURN_VALUE
    >>>


    Even if you're not familiar with Python's bytecode, I hope the simple
    disassembly above is clear enough anyway: the value of 'a' is "loaded"
    (onto the Python virtual machine's stack) as a *GLOBAL*, the value of
    'x' is loaded via the "fast" (locals-only) route, then the multiply
    happens (using the two top entries of the stack and pushing the result)
    and the result is returned as the function's value.

    Now, since you have the lambda available AS A STRING, you may consider:

    >>> def ff(a=99):

    .... return lambda x: x * a
    ....
    >>> thefunc1 = ff()
    >>> dis.dis(thefunc1)

    2 0 LOAD_FAST 0 (x)
    3 LOAD_DEREF 0 (a)
    6 BINARY_MULTIPLY
    7 RETURN_VALUE
    >>>


    Here, the Python compiler knows that a IS a local (an argument, in
    this example) in an enclosing function, therefore it loads 'a' with
    LOAD_DEREF, *NOT* with LOAD_GLOBAL. Because of this, and of this
    only, thefunc1.closure does matter (it's a cell referencing that
    int value, 99 in this case). Perhaps that might help, though it
    still takes QUITE a bit of work to exploit it.

    However, I do not understand why you should fight "the system" (the
    fact that the Python compiler KNOWS that 'a' is global in the first
    and simpler use) -- why not just supply that dict you have as (some
    part of) the globals...?


    Alex
     
    Alex Martelli, Sep 30, 2003
    #4
  5. =?ISO-8859-1?Q?Jean-S=E9bastien_Bolduc?=

    Peter Abel Guest

    (Jean-S?bastien Bolduc) wrote in message news:<>...
    > > **kwa** is the solution!
    > >
    > > In your factory-function *foo* you have to declare *a*
    > > keywordarguement which is initialized with a.
    > >
    > > >>> def foo():

    > > ... a=1
    > > ... f=lambda x,a=a:a*x
    > > ... return f

    >
    > Not exactly what I'm looking for, I'm afraid. A more complete picture
    > is this: I'm writing a class such as this:
    >
    > class Foo:
    > def __init__(self, fnc, **params):
    > ...
    > def evaluate(self, val)
    > ...
    >
    > That has to be instantiated as, e.g.:
    >
    > x = Foo( 'lambda x : a*x', dict( a = 2. ) )
    >
    > so that "x.evaluate( 5. )" will return, in this case, "2.*5.".
    >
    > The approach you describe will certainly work, but the thing is that
    > this class will have to be used by people who don't necessarily know
    > Python (yet). So I would really like the lambda function's parameter
    > list to always be the same, whatever you put on its RHS.
    >
    > Once again, I don't understand why the "eval", as described above with
    > "globals" and "locals" arguments specified, will not work. Is there a
    > way to modify a function's closure?
    >
    > Thanks,
    > JSeb


    Sorry, I missunderstood you.
    Since your last post I learned the further more two
    parameters of the eval-function. I'm even not sure
    if I checked the problem entirely, but let me try to
    explain what I think I may have understood.

    In the following function the eval-statement doesn't
    really need the locals() to evaluate the lambda-function.
    It really needs to tell the lambda where is its parent's
    namespace - what is by default the modules-namespace which
    one calls *globals()* - where to search for variables when
    not found in the locals().

    >>> def new_fn(fn_string):

    .... a=99999
    .... print locals()
    .... local_fun=eval(fn_string,globals(),locals())
    .... return local_fun
    ....

    I think the locals() are always the variables of a function's
    body - in the above case of the function *new_fn*.

    >>> f=new_fn('lambda x:(x*a,locals())')

    will show that:
    {'a': 99999, 'fn_string': 'lambda x:(x*a,locals())'}

    So the following let us know what locals() means
    for the lambda-function:
    >>> a=0
    >>> f(10)

    (0, {'x': 10})
    >>>

    But *a* is found in the global-scope.
    I'm not sure if this really helps you and the only
    workaround for me is something I posted to you the last time.

    What makes me more confused is the following example, where
    the lambda-function is declared explicitly.

    >>> def new_fn():

    .... a=99999
    .... print locals()
    .... local_fun=lambda x:(x*a,locals())
    .... return local_fun
    ....
    >>> f=new_fn()

    {'a': 99999}
    >>> a=0
    >>> f(10)

    (999990, {'a': 99999, 'x': 10})
    >>>


    But I think that's what you did already.

    PS: I here someone whispering something of *nested scopes*.

    Regards
    Peter
     
    Peter Abel, Sep 30, 2003
    #5
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Eric Newton
    Replies:
    3
    Views:
    9,422
    Brock Allen
    Apr 4, 2005
  2. Nike
    Replies:
    7
    Views:
    373
  3. In-Ho Yi
    Replies:
    1
    Views:
    252
    Vitaly Filimonov [MSFT]
    Dec 11, 2003
  4. Matt

    Invalid Procedure Call or Arguement

    Matt, May 5, 2006, in forum: ASP General
    Replies:
    5
    Views:
    197
    Anthony Jones
    May 5, 2006
  5. an an
    Replies:
    11
    Views:
    196
    an an
    Feb 9, 2008
Loading...

Share This Page