I'm trying to understand Python's exec() function - python

So I have a string which is a function like
code = """def somefn(x):
return x + x
y = somefn(z)"""
and I am trying to run this in another function like
def otherfn(codestr):
z = 2
exec(codestr)
return y
otherfn(code)
but it gives me the error:
Traceback (most recent call last):
File "C:/Users/Admin/Desktop/heh.py", line 11, in
otherfn(code)
File "C:/Users/Admin/Desktop/heh.py", line 9, in otherfn
return y
NameError: name 'y' is not defined
it works fine outside the function like
z=2
exec(codestr)
print(y)
it finds y just fine but not sure why it is bugging out when it is in the function.
How can I fix this? Is it something to do with globals() and locals()? Using Python 3.6 btw.

There are several problems with your code. First of all, you have an indentation problem - the y gets 'defined' inside the somefn() function, after the return so it never actually gets the chance to get onto the stack. You need to redefine your code to :
code = """def somefn(x):
return x + x
y = somefn(z)"""
But that's just the tip of the iceberg. The greater issue is that exec() cannot modify the local scope of a function. This is due the fact that Python doesn't use a dict for lookup of variables in the local scope so all changes from the exec() do not get reflected back to the stack to enable lookup. This causes a weird issue where exec() seemingly changes the locals() dictionary, but Python still throws a NameError:
def otherfn(codestr):
z = 2
exec(codestr)
print(locals()["y"]) # prints 4
return y # NameError
otherfn(code)
This is an intended behavior, as explained in the issue4831, and further pontificated in the official docs:
Note: The default locals act as described for function locals() below: modifications to the default locals dictionary should not be attempted. Pass an explicit locals dictionary if you need to see effects of the code on locals after function exec() returns.
But if you have to reflect the changes you can just do a post-exec local scope update:
def otherfn(codestr):
z = 2
locals_ = locals()
exec(codestr, globals(), locals_)
y = locals_["y"]
return y
otherfn(code)

Related

How can I dynamically define a function from user input?

I'm trying to dynamically generate a function from user input. The user gives a function as a string input, gives the differents parameters, and I want to write a function that will change this string into a usable function. My idea so far was to use the exec function to create a lambda function constructed with a string like so : exec("f = lambda {}:{}".format(', '.join(['x'] + parameter_list), function_string)). This would give, for example, a string like "f = lambda x, a:x+a" to the exec function.
This technique works fine if I define the scope of exec to globals(), but I'd like my function to be local so that I can do something like this:
def define_function(function_string, parameter_list):
exec("f = lambda {}:{}".format(', '.join(['x'] + parameter_list), function_string))
return f
or like this :
def define_function(function_string, parameter_list):
exec("return lambda {}:{}".format(', '.join(['x'] + parameter_list), function_string))
However, in the first case, it gives an error along the lines of "f is not defined". In the second case, meanwhile, it says that "return can only be used in a function". I understand why the second error occurs, but I wonder why my first try didn't work. Using globals() makes it work, but I'd rather not use it.
If you have any suggestions for other methods for achieving the same result, I'm interested in those as well.
EDIT
Using eval also raises an error :
line 9, in define_function
eval("f = lambda {}:{}".format(', '.join(['x'] + parameter_list), function_string))
File "<string>", line 1
f = lambda x, a:x+a
^
SyntaxError: invalid syntax
Solution 1: use eval():
def get_func(param_list, result_str):
return eval("lambda {}: {}".format(', '.join(['x'] + param_list), result_str))
Solution 2: if you really want to use exec(), do something like this:
def get_func(param_list, result_str):
exec("f = lambda {}: {}".format(', '.join(['x'] + param_list), result_str))
return locals()['f']
Some good info on the differences between exec and eval here: What's the difference between eval, exec, and compile?
I can see what you're trying to do in your question, by the way, and it's worth noting that the documentation pages for exec() and locals() both explicitly warn that this won't work. Ditto, if you type help(locals) into the interactive interpreter, you get this message:
Help on built-in function locals in module builtins:
locals()
Return a dictionary containing the current scope's local variables.
NOTE: Whether or not updates to this dictionary will affect name lookups in
the local scope and vice-versa is *implementation dependent* and not
covered by any backwards compatibility guarantees.

Input function interpreted as variable for some reason

Been looking around and can't find an answer.
So I call either raw_input() or input() to fill out a simple variable. But instead of running the function, I get
UnboundLocalError: local variable 'input' referenced before assignment
My code doesn't have 'input()' anywhere before that first call, and never makes a variable sharing the name. Same happens for 'raw_input()'. I'm confused here, as calls to the functions work fine in other code, and also on the console, but for some reason throw errors on this. No previous calls. Funny enough, it worked on my first run of the code, but now doesn't. What am I looking for?
Edit: Someone asked for the code. Not much to show.
end = False
lightsoff = []
lightson = []
bias = []
i = 0
while not end:
print "Set #" + str(i + 1) +", LED off: ",
offFile = input()
EDIT: To correct here, a variable named 'input' is referred to much later in the code (it's long). I wrote a couple of references backwards. I didn't know Python made a list (or something) of what's going to be a local variable before running, thus throwing up problems like this.
I suspect your code looks a bit like:
>>> def x():
... input = input()
... print(input)
...
>>> x()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in x
UnboundLocalError: local variable 'input' referenced before assignment
>>>
The problem is that you've created a variable called 'input', and then trying to get something from it before it exists.
>>> def y():
... val = input()
... print(val)
...
>>> y()
Would work a bit better. In python (and most dynamic languages) functions are kind of values too - so you can use the name of a function just like you would any other variable.
So a good idea is to not name a variable the same as a function.
Does that help?

Argument binding in closure function

def test():
x = 99
def nested(x):
print (x)
return nested
a = test()
a()
TypeError: nested() missing 1 required positional argument: 'x'
When I call nested to print argument x assigned in closure function test, the TypeError prompts me to pass a positional argument into nested, but why 'x' assigned in test function didn't be passed into nested?
def test():
x = 99
def nested():
print (x)
return nested
a = test()
a()
The function namespace is inherited automatically, you don’t have to pass it as an argument. If you include an argument into the function definition, then you have to obligatorily pass it a value when you call the function (unless you set a default value, for example:)
def test():
x = 99
def nested(y=100):
print (x,y)
return nested
a = test()
a() # will prinnt 99,100 because x is already inheritted in the namespace,
# and 100 is the fallback value
For the code you provide, I understand you kind of get the functioning of nested functions, but you fail to grasp the idea of namespaces. To tell from a namespace, a scope and a function/method argument, I recommend you some reading:
A scope refers to a region of a program where a namespace can be
directly accessed, i.e. without using a namespace prefix. In other
words: The scope of a name is the area of a program where this name
can be unambiguously used, for example inside of a function. A name's
namespace is identical to it's scope. Scopes are defined statically,
but they are used dynamically.
https://www.python-course.eu/namespaces.php
You may find this other link useful:
https://www.quora.com/What-is-the-difference-between-parameters-and-arguments-in-Python however best is always read Python docs for a precise and complete understanding ;)
When you do a = test(), you have called the test() function, and got the nested function. This function needs an argument, so you can't call it by a() alone (it's like calling it by nested()). You would need to modify this to def nested() for your code to work, and, more importantly, to see that the function "remembers" the x defined outside.
A more typical example would be
def test(x):
def nested(y):
print(f'x is {x}, y is {y}')
return nested
a = test(5)
a(12)

How can I change a global variable from within a function? [duplicate]

This question already has answers here:
Using global variables in a function
(25 answers)
UnboundLocalError trying to use a variable (supposed to be global) that is (re)assigned (even after first use)
(14 answers)
Closed 7 months ago.
I'm trying to add or subtract from a defined variable, but how can I overwrite the old value with the new one?
a = 15
def test():
a = a + 10
print(a)
test()
Error message:
Traceback (most recent call last):
File "test.py", line 7, in <module>
test()
File "test.py", line 4, in test
a = a +10
UnboundLocalError: local variable 'a' referenced before assignment
The error that you get when you try to run your code is:
UnboundLocalError: local variable 'a' referenced before assignment
… which, on the face of it, seems strange: after all, the first statement in the code above (a = 15) is an assignment. So, what's going on?
Actually, there are two distinct things happening, and neither of them are obvious unless you already know about them.
First of all, you actually have two different variables:
The a in your first line is a global variable (so called because it exists in the global scope, outside of any function definitions).
The a in the other lines is a local variable, meaning that it only exists inside your test() function.
These two variables are completely unrelated to each other, even though they have the same name.
A variable is local to a function if there's a statement assigning to it inside that function - for instance, your a = a +10 line.
Even so, the error still looks strange - after all, the very first thing you do inside test() is assign to a, so how can it be referenced beforehand?
The answer is that, in an assignment statement, Python evaluates everything on the right hand side of the = sign before assigning it to the name on the left hand side – so even though the assignment is written first in your code, a gets referenced first in that right hand side: a +10.
There are two ways you can get around this. The first is to tell Python that you really want the a inside test() to be the same a in the global scope:
def test():
global a
a = a + 10
print(a)
This will work, but it's a pretty bad way to write programs. Altering global variables inside functions gets hard to manage really quickly, because you usually have lots of functions, and none of them can ever be sure that another one isn't messing with the global variable in some way they aren't expecting.
A better way is to pass variables as arguments to functions, like this:
a = 15
def test(x):
x = x + 10
print(x)
test(a)
Notice that the name doesn't have to be the same - your new definition of test() just says that it accepts a value, and then does something with it. You can pass in anything you like – it could be a, or the number 7, or something else. In fact, your code will always be easier to understand if you try to avoid having variables with the same name in different scopes.
If you play with the code above, you'll notice something interesting:
>>> a = 15
>>> test(a)
25
>>> a
15
… a didn't change! That's because although you passed it into test() and it got assigned to x, it was then x that got changed, leaving the original a alone.
If you want to actually change a, you need to return your modified x from the function, and then reassign it back to a on the outside:
>>> a = 15
>>>
>>> def test(x):
... x = x + 10
... print(x)
... return x
...
>>> a = test(a)
25
>>> a
25
You're modifying a variable a created in the scope of the function test(). If you want the outer a to be modified, you could do:
a = 15
def test():
global a
a = a + 1
print(a)
test()
I would do it this way:
def test(a):
a = a +10
return a
print(test(15))
Note that in the version hereby proposed there are some things differing from yours.
First, what I wrote down would create a function that has, as an input, the value a (in this case set to 15 when we call the function -already defined in the last line-), then assigns to the object a the value a (which was 15) plus 10, then returns a (which has been modified and now is 25) and, finally, prints a out thanks to the last line of code:
print(test(15))
Note that what you did wasn't actually a pure function, so to speak. Usually we want functions to get an input value (or several) and return an input value (or several). In your case you had an input value that was actually empty and no output value (as you didn't use return). Besides, you tried to write this input a outside the function (which, when you called it by saying test(a) the value a was not loaded an gave you the error -i.e. in the eyes of the computer it was "empty").
Furthermore, I would encourage you to get used to writing return within the function and then using a print when you call it (just like I wrote in the last coding line: print(test(15))) instead of using it inside the function. It is better to use print only when you call the function and want to see what the function is actually doing.
At least, this is the way they showed me in basic programming lessons. This can be justified as follows: if you are using the return within the function, the function will give you a value that can be later used in other functions (i.e. the function returns something you can work with). Otherwise, you would only get a number displayed on screen with a print, but the computer could not further work with it.
P.S. You could do the same doing this:
def test(a):
a +=10
return a
print(test(15))
a = 15
def test():
global a
a = a + 10
print(a)
test()
If you want to change the values of local variables inside a function you need to use the global keyword.
The scope of the variable is local to the block unless defined explicitly using keyword global. There is another way to access the global variable local to a function using the globals function:
a = 15
def test():
a = globals()['a']
a += 10
print(a)
test()
The above example will print 25 while keeping the global value intact, i.e., 15.
The issue here is scope.
The error basically means you're using the variable a, but it doesn't exist yet. Why? The variable a has not been declared in the test() function. You need to "invite" global variables before using them.
There are several ways to include a variable in the scope of a function. One way is to use it as a parameter like this:
def test(number):
number = number + 10
print(number)
Then implement it as:
test(a)
Alternatively, you could also use the global keyword to show the function that a is a global variable. How? Like this:
def test():
global a
a = a + 10
print(a)
Using the global keyword just tells the test where to look for a.
With the first method, you can't modify the global variable a, because you made a copy of it and you're using the copy. In the second method, though, any changes you make in the function test() to a, will affect the global variable a.
# All the mumbo jumbo aside, here is an experiment
# to illustrate why this is something useful
# in the language of Python:
a = 15 # This could be read-only, should check docs
def test_1():
b = a + 10 # It is perfectly ok to use 'a'
print(b)
def test_2():
a = a + 10 # Refrain from attempting to change 'a'
print(a)
test_1() # No error
test_2() # UnboundLocalError: ...
Your error has nothing to do with it being already defined…
A variable is only valid inside its so-called scope: If you create a variable in a function it is only defined in this function.
def test():
x = 17
print(x) # Returns 17
test()
print(x) # Results in an error.

python function: scope of function parameter initialized with default value [duplicate]

This question already has answers here:
"Least Astonishment" and the Mutable Default Argument
(33 answers)
Closed 8 years ago.
I have a python programming question, I am using a caching pattern to speed up computation, here is an example code:
def f(a=1, a_dict={}):
try:
b=a_dict['one']
print 'fetched from cache'
except:
b=10
a_dict['one']=10
print 'put in cache'
return b
Now if I call this function the first time the result is:
>>> f(1)
put in cache
10
I call again:
>>> f(1)
fetched from cache
10
This is nice a behaviour since it uses caching. However, I find it strange because the variable a_dict has been defined within the function, so once the function is over it should be out of scope... Note that this variable is not visible from outside the function:
>>> a_dict
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'a_dict' is not defined
I would have guessed that when I call the function the second time, the default variable a_dict should have been initialized to {}, so I find it strange that when I call f() the second time the previous value of a_dict is somehow still in scope.
In summary, I would have expected to achieve the desired behaviour like this:
the_dict={}
f(1, the_dict)
# call second time
f(1, the_dict)
Because the object the_dict is passed by reference and hence stays in scope.
Can someone please explain the semantics of parameter initialization in a function and their scope?
Functions are objects in python, so default parameters can be thought of as 'members'. It is explained fully: http://effbot.org/zone/default-values.htm
To get the behaviour you expect, you can have the function defined as follows:
def f(a=1, a_dict=None):
if a_dict is None:
a_dict = {}
try:
b=a_dict['one']
print 'fetched from cache'
except:
b=10
a_dict['one']=10
print 'put in cache'
return b
This means that, rather than the {} being made when the function is defined (and therefore retained in it's object def), it's made when the function is run (and therefore is a local variable).

Categories

Resources