Python the function locals() was changed by just calling it - python

please read the two code ,and i find the only different is printing the locals() or not. But one of them is wrong.
Please help me , thanks
import numpy as np
class Solution:
def solve(self, f, a, b, n):
x = np.linspace(a,b,n)
# print(locals())
loc = locals()
fstr = '''
def fun(x):
return %s
''' % f
exec(fstr)
# print(locals())
fun = loc['fun']
y = fun(x)
print(x,y,sep = '\n')
a = Solution()
a.solve('x+1',-5,5,5)
In this code ,I didn't print the locals()
if I only print it and write '#' in front of "fun = loc['fun']" and "y = fun(x)" ,there is a key named 'fun' in the output of locals()
import numpy as np
class Solution:
def solve(self, f, a, b, n):
x = np.linspace(a,b,n)
# print(locals())
loc = locals()
fstr = '''
def fun(x):
return %s
''' % f
exec(fstr)
print(locals())
fun = loc['fun']
y = fun(x)
print(x,y,sep = '\n')
a = Solution()
a.solve('x+1',-5,5,5)
But in this code ,i can't find the key named 'fun' in the output of locals()
Traceback (most recent call last):
File "tmp.py", line 20, in <module>
a.solve('x+1',-5,5,5)
File "tmp.py", line 15, in solve
fun = loc['fun']
KeyError: 'fun'
All of this seem that "fun = loc['fun']" and "y = fun(x)" determine the output of locals()
but i think it is impossible for python that latter code can change the front code

Yeah, that happens with locals(). locals() is confusing and not well documented.
Calling locals() repeatedly in the same stack frame returns the same dict every time, and every call to locals() updates that dict with the current values of local (or closure) variables. The dict is attached to the stack frame as its f_locals attribute, and accessing that attribute will also update the dict.
To use locals() safely without the values changing unpredictably, you should copy the returned dict:
current_locals = locals().copy()
Otherwise, even running your code in a debugger could change its behavior, since debuggers typically access f_locals to inspect local variables.
Also, trying to exec code that assigns any variables in a local scope is officially unsupported and behaves weirdly, and def counts as an assignment. You shouldn't use exec for this anyway.

Related

Why doesn't python delete a function's variables when it it out of scope?

In python, I know that functions run in the context that they are called from, not the context that they are defined in. However, in the code below, where a function is returned from another function, it still has access to the variables (specifically the list 'cache') of the function it was defined in, even though that function is now out of scope (I think), and so I would have though it's variables would have been deleted. But it can also access global variables from the current context?
def wrapper(func):
cache = []
def func_new(n):
cache.append(func(n))
print(cache)
return func_new
def add1(n):
return(n+1)
x = wrapper(add1)
x(5)
x(2)
print(cache)
It doesn't?
>>> x = wrapper(add1)
>>> x(5)
[6]
>>> x(2)
[6, 3]
>>> print(cache)
Traceback (most recent call last):
File "<pyshell#7>", line 1, in <module>
print(cache)
NameError: name 'cache' is not defined
As for the cache variable being defined within the wrapper, that matches behavior elsewhere; the function has access to all variables where it is defined. The opposite is not true; variables defined in the function are not accessible in the context scope unless explicitly a global.
Does this example help?
>>> x = 4
>>> def test():
print(x)
>>> test()
4

Can a function know how it has been called?

Is there a way to know within a function if the function has been called by itself or assigned to a variable with = ?
I would like to do something like this:
def func():
if 'assigned with equal':
return 5
else:
print 'not assigned'
that would give those outputs:
func()
-> 'not assigned'
a = func()
a
-> 5
Yes, there is a way to do this, though getting it right will be tricky. You can use the inspect module to access the call stack. This allows you to see what the code looks like that called the function.
The stack looks something like this:
[(<frame object at 0x107de1d38>, 'path/to/function/file.py', 6, 'func', ['\tstack = inspect.stack()\n'], 0), (<frame object at 0x107d34050>, 'path/to/calling/file.py', 17, '<module>', ['func()\n'], 0)]
Notice the second to last entry: ['func()\n']. This is showing the code that calls your function. Even though the name of the function shows up elsewhere in the stack, it always shows the actual name of the function no matter how it is called. So you have to do a little work on your own to determine whether or not the call was made directly or through an assigned variable.
This is the only way to get the function name. Python does not have a feature to retrieve the function name from within the function itself.
To make it clear that this will be more difficult than just if func_name in stack, I've fleshed out the function a little bit to show a couple of real world examples of how a function might be called and what it would look like. Because the function can't determine its own name, it is hardcoded at the top of the function.
import inspect
def func(var=None, default=''):
my_name = 'func'
stack = inspect.stack()
func_call = stack[-1][4][0]
print func_call.rstrip() # `func_call` contains a trailing newline
# logic to determine if the name matches
# ...
# ...
x = func
func()
return_value = x(var=1)
print func()
if x():
pass
This prints:
func()
return_value = x(var=1)
print func()
None # This prints from the call, because `func()` doesn't return anything
if x():
Here is a running example I wrote based on the accepted answer. We can't assign a new name to the function but it does what I wanted to do
import inspect
# generator function to get all the strings in an iterable object
def descend_strings(obj):
if hasattr(obj,'__iter__'):
if type(obj)==dict:
for key in obj:
for result in descend_strings(obj[key]):
yield result
else:
for item in obj:
for result in descend_strings(item):
yield result
if type(obj)==str:
yield obj
def func():
stack = inspect.stack()
joined_flat_stack_str = ''.join(list(descend_strings(stack)))
if ('= func()' in joined_flat_stack_str) or ('print func()' in joined_flat_stack_str):
return 5
else:
print 'not assigned'
func() # 'not assigned'
a = func()
print a # 5
print func() # 5
x = func
x() # 'not assigned'
a = x() # 'not assigned'
print a # None

Remembered values, and scope gone from memory, in a Python Closure

Below is a simple piece of code I found in this tutorial.
Here's a nice definition of Closure I found here: "a function object that remembers values in enclosing scopes regardless of whether those scopes are still present in memory."
I gather that rotate() below is a closure. Please help me understand what values is it remembering even after their scope is gone from memory (and why does their scope leave memory)?
def make_rotater(seq):
def rotate():
val = seq.pop(0)
seq.append(val)
return val
return rotate
r = make_rotater([1,2,3])
r()
# 1
r()
# 2
(Update) Part 2: Why does the (closure-less) code below not work?
def make_rotater(seq):
val = seq.pop(0)
seq.append(val)
return val
r = make_rotater([1,2,3])
r()
# File "<stdin>", line 1, in <module>
# TypeError: 'int' object is not callable
It remembers local values from make_rotator so if u do:
def make_rotater():
seq=[1,2,3]
def rotate():
val = seq.pop(0)
seq.append(val)
return val
return rotate
The seq is referenced by rotate, so it will remain in memory for when you will call rotate, even though it was defined in make_rotater (that is already done, and cleaned from memory)
When you call make_rotater it creates a new seq and defines the rotate method that references seq, so one you leave make_rotater it's memory isn't needed except seq, (cause rotate still uses it). When you will no longer reference rotate, seq will also be cleaned
part 2:
your method now doesnt return another method it returns the number directly, so you dont need to do r()
you can use it like this:
seq = [1,2,3]
def make_rotater(seq):
val = seq.pop(0)
seq.append(val)
return val
r = make_rotater(seq)
print r # prints 1, note there is no r() just r
r = make_rotater(seq)
print r # prints 2
r = make_rotater(seq)
print r # prints 3
That definition is sort of right, sort of wrong. It depends on what you mean by a scope being "still present in memory". I'd say a better definition would be "a function object that remembers variables in enclosing scopes regardless of whether those scopes are still present on the call stack."
When you call make_rotater:
def make_rotater(seq):
def rotate():
val = seq.pop(0)
seq.append(val)
return val
return rotate
The rotate closure keeps the seq variable alive even after execution leaves the scope of make_rotater. Ordinarily, when execution leaves a function, its local variables would cease to exist.

Variable scope in nested functions

Could someone explain why the following program fails:
def g(f):
for _ in range(10):
f()
def main():
x = 10
def f():
print x
x = x + 1
g(f)
if __name__ == '__main__':
main()
with the message:
Traceback (most recent call last):
File "a.py", line 13, in <module>
main()
File "a.py", line 10, in main
g(f)
File "a.py", line 3, in g
f()
File "a.py", line 8, in f
print x
UnboundLocalError: local variable 'x' referenced before assignment
But if I simply change the variable x to an array, it works:
def g(f):
for _ in range(10):
f()
def main():
x = [10]
def f():
print x[0]
x[0] = x[0] + 1
g(f)
if __name__ == '__main__':
main()
with the output
10
11
12
13
14
15
16
17
18
19
The reason I am confused is, if from f() it can't access x, why it becomes accessible if x is an array?
Thanks.
But this answer says the problem is with assigning to x. If that's it,
then printing it should work just fine, shouldn't it?
You have to understand the order in which things happen. Before your python code is even compiled and executed, something called a parser reads through the python code and checks the syntax. Another thing the parser does is mark variables as being local. When the parser sees an assignment in the code in a local scope, the variable on the lefthand side of the assignment is marked as local. At that point, nothing has even been compiled yet--let alone executed, and therefore no assignment takes place; the variable is merely marked as a local variable.
After the parser is finished, the code is compiled and executed. When execution reaches the print statement:
def main():
x = 10 #<---x in enclosing scope
def f():
print x #<-----
x = x + 1 #<-- x marked as local variable inside the function f()
the print statement looks like it should go ahead and print the x in the enclosing scope (the 'E' in the LEGB lookup process). However, because the parser previously marked x as a local variable inside f(), python does not proceed past the local scope (the 'L' in the LEGB lookup process) to lookup x. Because x has not been assigned to in the local scope at the time 'print x' executes, python spits out an error.
Note that even if the code where an assignment occurs will NEVER execute, the parser still marks the variable on the left of an assignment as a local variable. The parser has no idea about how things will execute, so it blindly searches for syntax errors and local variables throughout your file--even in code that can never execute. Here are some examples of that:
def dostuff ():
x = 10
def f():
print x
if False: #The body of the if will never execute...
a b c #...yet the parser finds a syntax error here
return f
f = dostuff()
f()
--output:--
File "1.py", line 8
a b c
^
SyntaxError: invalid syntax
The parser does the same thing when marking local variables:
def dostuff ():
x = 10
def f():
print x
if False: #The body of the if will never execute...
x = 0 #..yet the parser marks x as a local variable
return f
f = dostuff()
f()
Now look what happens when you execute that last program:
Traceback (most recent call last):
File "1.py", line 11, in <module>
f()
File "1.py", line 4, in f
print x
UnboundLocalError: local variable 'x' referenced before assignment
When the statement 'print x' executes, because the parser marked x as a local variable the lookup for x stops at the local scope.
That 'feature' is not unique to python--it happens in other languages too.
As for the array example, when you write:
x[0] = x[0] + 1
that tells python to go lookup up an array named x and assign something to its first element. Because there is no assignment to anything named x in the local scope, the parser does not mark x as a local variable.
The reason is in first example you used an assignment operation, x = x + 1, so when the functions was defined python thought that x is local variable. But when you actually called the function python failed to find any value for the x on the RHS locally, So raised an Error.
In your second example instead of assignment you simply changed a mutable object, so python will never raise any objection and will fetch x[0]'s value from the enclosing scope(actually it looks for it firstly in the enclosing scope, then global scope and finally in the builtins, but stops as soon as it was found).
In python 3x you can handle this using the nonlocal keyword and in py2x you can either pass the value to the inner function or use a function attribute.
Using function attribute:
def main():
main.x = 1
def f():
main.x = main.x + 1
print main.x
return f
main()() #prints 2
Passing the value explicitly:
def main():
x = 1
def f(x):
x = x + 1
print x
return x
x = f(x) #pass x and store the returned value back to x
main() #prints 2
Using nonlocal in py3x:
def main():
x = 1
def f():
nonlocal x
x = x + 1
print (x)
return f
main()() #prints 2
The problem is that the variable x is picked up by closure. When you try to assign to a variable that is picked up from the closure, python will complain unless you use the global or nonlocal1 keywords. In the case where you are using a list, you're not assigning to the name -- You can modify an object which got picked up in the closure, but you can't assign to it.
Basically, the error occurs at the print x line because when python parses the function, It sees that x is assigned to so it assumes x must be a local variable. When you get to the line print x, python tries to look up a local x but it isn't there. This can be seen by using dis.dis to inspect the bytecode. Here, python uses the LOAD_FAST instruction which is used for local variables rather than the LOAD_GLOBAL instruction which is used for non-local variables.
Normally, this would cause a NameError, but python tries to be a little more helpful by looking for x in func_closure or func_globals 2. If it finds x in one of those, it raises an UnboundLocalError instead to give you a better idea about what is happening -- You have a local variable which couldn't be found (isn't "bound").
1python3.x only
2python2.x -- On python3.x, those attributes have changed to __closure__ and __globals__ respectively
The problem is in the line
x = x + 1
This is the first time x being assigned in function f(), telling the compiler that x is a local name. It conflicts with the previous line print x, which can't find any previous assignment of the local x.
That's where your error UnboundLocalError: local variable 'x' referenced before assignment comes from.
Note that the error happens when compiler tries to figure out which object the x in print x refers to. So the print x doesn't executes.
Change it to
x[0] = x[0] + 1
No new name is added. So the compiler knows you are referring to the array outside f().

Python 2.7 using the input to a function as a string and variable

I would like to do the following:
print "CC =",CC
but as a function so that i only have to write the variable CC once. I can't work out how to do this in a function as it always evaluates CC as a floating point number (which it is).... Is there a way to accept the input to a function as both a string and floating point number?
I tried this:
def printme(a):
b='%s' % a
print b
return b
but of course it only prints the value of a, not its name.
You could use the inspect module (see also this SO question):
def printme(x):
import inspect
f = inspect.currentframe()
val = f.f_back.f_locals[x]
print x, '=', val
CC = 234.234
printme('CC') # <- write variable name only once
# prints: CC = 234.234
Perhaps a dictionary is a better approach to the problem. Assuming you have several name-value pairs that you want to use, you can put them in a dict:
params = {"CC": 1.2345, "ID": "Yo!", "foo": "bar"}
Then, for example, you could print all the names and values nicely formatted like this:
for key in params:
print "{0} = {1}".format(key, params[key])
But since it is still unclear why you are trying to do this, it's hard to tell whether this is the right way.
I think this is your required solution:
def printme(x):
keys_list = [key for key, value in globals().iteritems() if value == x]
print keys_list
for key in keys_list:
if id(globals()[key]) == id(x):
result = "%s = %s" %(key, x)
print result
break
return result
for example if you declare a variable:
>>> c=55.6
then result of printme(c) will be
>>> 'c = 55.6'
Note: This solution is based on globally unique id matching.
Not exactly what you want, but easy to do:
def printme(**kwargs):
for key, value in kwargs.items():
print '%s=%s' % (key, value)
return value
In [13]: printme(CC=1.23, DD=2.22)
CC=1.23
DD=2.22
Out[13]: 1.23
If I understand you correctly you want something like this?
def f(a):
print('{0}: = {1}'.format(locals().keys()[0], a))
Update:
I am aware that the example doesn't make a lot of sense, as it's basically the same as:
def f(a):
print('a: {0}'.format(a))
I merely wanted to point the OP to locals() as I didn't quite understand what he's trying to accomplish.
I guess this is more what he's looking for:
def f(**kwargs):
for k in kwargs.keys():
print('{0}: {1}'.format(k, kwargs[k]))
f(a=1, b=2)
If I understand you correctly you want a shorthand for printing a variable name and its value in the current scope? This is in general impossible without using the interpreters trace function or sys._getframe, which should in general only be used if you know what you're doing. The reason for this is that the print function has no other way of getting the locals from the calling scope:
def a():
x = 1
magic_print("x") #will not work without accessing the current frame
What you CAN do without these is explicitly pass the locals to a function like this:
def printNameAndValue(varname, values):
print("%s=%s" % (varname, values[varname]))
def a():
x = 1
printNameAndValue("x", locals()) #prints 'x=1'
EDIT:
See the answer by catchemifyoutry for a solution using the inspect module (which internally uses sys._getframe). For completeness a solution using the trace function directly - useful if you're using python 2.0 and inspect isn't available ;)
from sys import settrace
__v = {} #global dictionary that holds the variables
def __trace(frame, event, arg):
""" a trace function saving the locals on every function call """
global __v
if not event == "call":
return __trace
__v.update(frame.f_back.f_locals)
def enableTrace(f):
""" a wrapper decorator setting and removing the trace """
def _f(*a, **kwa):
settrace(__trace)
try:
f(*a, **kwa)
finally:
settrace(None)
return _f
def printv(vname):
""" the function doing the printing """
global __v
print "%s=%s" % (vname, __v[vname])
Save it in a module and use like this:
from modulenamehere import enableTrace, printv
#enableTrace
def somefunction():
x = 1
[...]
printv("x")
used a global variable to achieve this,func.__globals__.keys() contains all the variables passed to func, so I filtered out the name startin with __ and stored them in a list.
with every call to func() the func.__globals__.keys() gets updated with the new variable name,so compare the new varn with the older glo results in the new variable that was just added.
glo=[]
def func(x):
global glo
varn=[x for x in func.__globals__.keys() if not x.startswith('__') and x!=func.__name__]
new=list(set(varn)^set(glo))
print("{0}={1}".format(new[0],x))
glo=varn[:]
output:
>>> a=10
>>> func(a)
a=10
>>> b=20
>>> func(20)
b=20
>>> foo='cat'
>>> func(foo)
foo=cat
>>> bar=1000
>>> func(bar)
bar=1000

Categories