eval's local arguement ignored?

  • Thread starter =?ISO-8859-1?Q?Jean-S=E9bastien_Bolduc?=
  • Start date
?

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

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
 
P

Peter Abel

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.
.... a=1
.... f=lambda x,a=a:a*x
.... return f
3

A global *a* doesn't affect your function.3

but you even can get influence to *a*:
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 !!300

To change the global *a* doesn't affect your lambda-function!300

But you can give your function the additional information.3

or more traditional3

And the default kwa will stay resident.
Regards
Peter
 
J

Jean-S?bastien Bolduc

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

... 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
 
A

Alex Martelli

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...:
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:
.... return lambda x: x * a
.... 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
 
P

Peter Abel

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().
.... 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*.
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: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.
.... a=99999
.... print locals()
.... local_fun=lambda x:(x*a,locals())
.... return local_fun
....
But I think that's what you did already.

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

Regards
Peter
 

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,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top