I'm tinkering around with Python. I have two functions. The first one calls the second, and from the second I am trying to get the value of a local variable within the first, like so:
def b():
local_var = 8
a()
def a():
#get b::local_var here?
I understand that it is possible in python to print out the stack, but I was wondering about accessing the variables and memory within those functions.
Is this even possible?
yes you can, just pass the variable in the function
def b():
local_var = 8
a(local_var) #1
def a(LV): #2
print LV
1
you passed the variable
2
created a new variable LV and assigned the local_var value to LV
Variables that are defined inside a function body have a local scope, and those defined outside have a global scope.
This means that local variables can be accessed only inside the function in which they are declared, whereas global variables can be accessed throughout the program body by all functions. When you call a function, the variables declared inside it are brought into scope.
So in this case you can use 2 way :
1. define a global variable :
>>> def b():
... global local_var
... local_var=8
... a()
...
>>> def a():
... print local_var
...
>>> a
8
2.pass the variable in a() as its argument :
>>> def b():
... local_var=8
... a(local_var)
...
>>> def a(arg):
... print arg
...
>>> a
8
Related
class Something:
x = "hi"
def func(self):
k = "hi2"
In this piece of code, x as a class attribute and k as a variable. What scope (local, enclosed, global, builtin) would x belong to and what scope would k belong to?
You say you don't want an answer like "in the scope of the function" or "-- the class", but that would be the most precise answer. A scope like local is always relative to where you are.
If you are in the global scope, then only the class itself is in both the local and global scope (which are the same then), but neither of the variables are. You can access x via the class, but k will only be defined when the function is called.
If you are inside of func, then k and self are in the local scope, but x is in neither the local nor global scope. It is not in the enclosed scope either; it can not be accessed directly, as in print(x), but only via the instance self or the class Something.
class Something:
x = "hi"
def func(self):
k = "hi2"
print(locals()) # contains k, self
print(globals()) # contains Something
print(k) # works
print(self.x) # works
print(Something.x) # works
print(x) # does not work
Something().func()
The case is different with nested functions. Here, variables defined in the outer functions are in the "enclosing scope", but may be promoted to the local scope by using them:
def f():
a = []
def g():
b = None
# a = [] # define a new a in local scope?
# a.append(42) # without above line, this changes enclosed a
print(locals()) # only b, unless you use a here
g()
print(a)
f()
if you leave the commented lines as they are, only b in in the inner local scope
if you activate the append line, a from the enclosing scope is moved to the local scope and changed in both scopes
if you activate both commented lines, a new a is defined in the local scope without changing the a in the enclosing scope
Consider this example:
def A():
b = 1
def B():
# I can access 'b' from here.
print(b)
# But can i modify 'b' here?
B()
A()
For the code in the B function, the variable b is in a non-global, enclosing (outer) scope. How can I modify b from within B? I get an UnboundLocalError if I try it directly, and using global does not fix the problem since b is not global.
Python implements lexical, not dynamic scope - like almost all modern languages. The techniques here will not allow access to the caller's variables - unless the caller also happens to be an enclosing function - because the caller is not in scope. For more on this problem, see How can I access variables from the caller, even if it isn't an enclosing scope (i.e., implement dynamic scoping)?.
On Python 3, use the nonlocal keyword:
The nonlocal statement causes the listed identifiers to refer to previously bound variables in the nearest enclosing scope excluding globals. This is important because the default behavior for binding is to search the local namespace first. The statement allows encapsulated code to rebind variables outside of the local scope besides the global (module) scope.
def foo():
a = 1
def bar():
nonlocal a
a = 2
bar()
print(a) # Output: 2
On Python 2, use a mutable object (like a list, or dict) and mutate the value instead of reassigning a variable:
def foo():
a = []
def bar():
a.append(1)
bar()
bar()
print a
foo()
Outputs:
[1, 1]
You can use an empty class to hold a temporary scope. It's like the mutable but a bit prettier.
def outer_fn():
class FnScope:
b = 5
c = 6
def inner_fn():
FnScope.b += 1
FnScope.c += FnScope.b
inner_fn()
inner_fn()
inner_fn()
This yields the following interactive output:
>>> outer_fn()
8 27
>>> fs = FnScope()
NameError: name 'FnScope' is not defined
I'm a little new to Python, but I've read a bit about this. I believe the best you're going to get is similar to the Java work-around, which is to wrap your outer variable in a list.
def A():
b = [1]
def B():
b[0] = 2
B()
print(b[0])
# The output is '2'
Edit: I guess this was probably true before Python 3. Looks like nonlocal is your answer.
No you cannot, at least in this way.
Because the "set operation" will create a new name in the current scope, which covers the outer one.
I don't know if there is an attribute of a function that gives the __dict__ of the outer space of the function when this outer space isn't the global space == the module, which is the case when the function is a nested function, in Python 3.
But in Python 2, as far as I know, there isn't such an attribute.
So the only possibilities to do what you want is:
1) using a mutable object, as said by others
2)
def A() :
b = 1
print 'b before B() ==', b
def B() :
b = 10
print 'b ==', b
return b
b = B()
print 'b after B() ==', b
A()
result
b before B() == 1
b == 10
b after B() == 10
.
Nota
The solution of Cédric Julien has a drawback:
def A() :
global b # N1
b = 1
print ' b in function B before executing C() :', b
def B() :
global b # N2
print ' b in function B before assigning b = 2 :', b
b = 2
print ' b in function B after assigning b = 2 :', b
B()
print ' b in function A , after execution of B()', b
b = 450
print 'global b , before execution of A() :', b
A()
print 'global b , after execution of A() :', b
result
global b , before execution of A() : 450
b in function B before executing B() : 1
b in function B before assigning b = 2 : 1
b in function B after assigning b = 2 : 2
b in function A , after execution of B() 2
global b , after execution of A() : 2
The global b after execution of A() has been modified and it may be not whished so
That's the case only if there is an object with identifier b in the global namespace
The short answer that will just work automagically
I created a python library for solving this specific problem. It is released under the unlisence so use it however you wish. You can install it with pip install seapie or check out the home page here https://github.com/hirsimaki-markus/SEAPIE
user#pc:home$ pip install seapie
from seapie import Seapie as seapie
def A():
b = 1
def B():
seapie(1, "b=2")
print(b)
B()
A()
outputs
2
the arguments have following meaning:
The first argument is execution scope. 0 would mean local B(), 1 means parent A() and 2 would mean grandparent <module> aka global
The second argument is a string or code object you want to execute in the given scope
You can also call it without arguments for interactive shell inside your program
The long answer
This is more complicated. Seapie works by editing the frames in call stack using CPython api. CPython is the de facto standard so most people don't have to worry about it.
The magic words you are probably most likely interesed in if you are reading this are the following:
frame = sys._getframe(1) # 1 stands for previous frame
parent_locals = frame.f_locals # true dictionary of parent locals
parent_globals = frame.f_globals # true dictionary of parent globals
exec(codeblock, parent_globals, parent_locals)
ctypes.pythonapi.PyFrame_LocalsToFast(ctypes.py_object(frame),ctypes.c_int(1))
# the magic value 1 stands for ability to introduce new variables. 0 for update-only
The latter will force updates to pass into local scope. local scopes are however optimized differently than global scope so intoducing new objects has some problems when you try to call them directly if they are not initialized in any way. I will copy few ways to circumvent these problems from the github page
Assingn, import and define your objects beforehand
Assingn placeholder to your objects beforehand
Reassign object to itself in main program to update symbol table: x = locals()["x"]
Use exec() in main program instead of directly calling to avoid optimization. Instead of calling x do: exec("x")
If you are feeling that using exec() is not something you want to go with you can
emulate the behaviour by updating the the true local dictionary (not the one returned by locals()). I will copy an example from https://faster-cpython.readthedocs.io/mutable.html
import sys
import ctypes
def hack():
# Get the frame object of the caller
frame = sys._getframe(1)
frame.f_locals['x'] = "hack!"
# Force an update of locals array from locals dict
ctypes.pythonapi.PyFrame_LocalsToFast(ctypes.py_object(frame),
ctypes.c_int(0))
def func():
x = 1
hack()
print(x)
func()
Output:
hack!
I don't think you should want to do this. Functions that can alter things in their enclosing context are dangerous, as that context may be written without the knowledge of the function.
You could make it explicit, either by making B a public method and C a private method in a class (the best way probably); or by using a mutable type such as a list and passing it explicitly to C:
def A():
x = [0]
def B(var):
var[0] = 1
B(x)
print x
A()
For anyone looking at this much later on a safer but heavier workaround is. Without a need to pass variables as parameters.
def outer():
a = [1]
def inner(a=a):
a[0] += 1
inner()
return a[0]
You can, but you'll have to use the global statment (not a really good solution as always when using global variables, but it works):
def A():
global b
b = 1
def B():
global b
print( b )
b = 2
B()
A()
def foo():
global a
a = 10
vs
a = 0
def foo():
a = 10
What is the use of declaring global in a function instead of declaring variables right at the top. It should be cleaner this way ?
Q. What is the use of declaring global in a function instead of declaring variables right at the top?
Generally we define few regularly used constants as global variables.
>>> import math
>>> math.pi
3.141592653589793
Mathematical contants/Web Page/Servernames/URLs - which wont change over a period of time.
In general, it is not recommended to RE-DEFINE global variable or define global variables with in a function. But few exception cases are there where we are left with no option but to re-define it. Could be due to a new upgrade in old system where this variable is heavily used across.
So please go with your second approach. Define 'a' outside your
function. This also looks good & easy to read.
>>> a = 10
>>> def foo():
... a = 14 # defining a local variable called a
...
>>> a
10
>>>
>>> foo()
>>> a
10
>>> # a = 14 has no scope outside foo function
>>>
>>> def foo():
... global a # notify function to use global variable here.
... a = 15 # re-defining global ==> nt preferred it
...
>>> a
10
>>> foo()
>>> a # see now it changed here.
15
Hope this helps.
In the first case the value remains same in all the functions.
def set_globvar_to_one():
global globvar
globvar = 1
print globvar
def print_globvar():
print globvar # No need for global declaration to read value of globvar
set_globvar_to_one()
print_globvar()
The output will be 1 and 1.
But 2nd case
def set_globvar_to_one():
globvar = 1
print globvar
def print_globvar():
print globvar # No need for global declaration to read value of globvar
set_globvar_to_one()
print_globvar()
Here you get error saying globvar is not defiend, coz there is no local or global variable named globavar
That would make it ambiguous. Look at this,
a = 0
def foo():
a = 10 # *
Look at the line marked *. Are you assigning to the global a or is this a local variable a with a value 10. The interpreter has no way to knowing what you meant. Thus, the need for global.
a = 0
def foo():
a = 10 # local variable a
def bar():
global a # will be modifying global variable a in this function
a = 10 # assign 10 to global variable
In your first example "a" is not available before foo() is called. Once it is defined in the function it available outside of the function.
In the second example it is available before foo() is called butchanges to it inside the function do not affect the global version .
This example should demonstrate the difference between the two examples
#!/usr/bin/python
def foo():
global a
a = 10
print a
try:
print a
except Exception,e:
print e
foo()
print a
print "-"*70
b = 0
def bar():
b = 10
print b
print b
bar()
print b
Output:
global name 'a' is not defined
10
10
----------------------------------------------------------------------
0
10
0
If you declare it as a global, you don't have to return it.
If you don't use global, you have the function to return a.
Without global it be like this:
In [7]: def foo():
...: a = 10
...: return a
...:
In [9]: a = foo()
In [10]: a
Out[10]: 10
With global:
In [11]: a = 0
In [12]: def foo2():
....: global a
....: a = 10
....:
In [13]: a
Out[13]: 0
In [14]: foo2()
In [15]: a
Out[15]: 10
It is you who decide if you want your function to return or to use global.
It can simplify you code, it can be useful for function that calculates some constants that you don't want to call it each time.
But if use it for single procedure, prefer "return" method.
if you are using the same variable inside the loop then there will be a competition between the global and local variable . so if you want to use the value of the global variable in the loop you have to mention the 'global' keyword
Firstly, it's related to this function:
a = 3
def b():
a = a+1
return a
returns an error.
Isn't it when the local environment of b couldn't find 'a', then it goes to the parent environment where b is defined, that is, where 'a' is found to be 3?
Why must I reference it with a global a in order for it to work? Meaning, do I have to do this only when the immediate parent environment is the global environment? Because the below function seems to be able to reference the parent environment:
def b():
def c():
print c
return c()
This is such a common issue that is is actually in the Python FAQ:
[...] when you make an assignment to a variable in a scope, that variable becomes local to that scope and shadows any similarly named variable in the outer scope. Since the last statement in foo assigns a new value to x, the compiler recognizes it as a local variable.
So, Python sees you're assigning to a local variable with a = .. and thinks, hey, a local variable, I'm fine with this. But, when it actually bumps into the content of that assignment (a + 1) and sees the same name a it complains because you're trying to reference it before assigning it.
This is why the following doesn't raise an error:
def b():
a = 20
a = a + 2
return a
You first make an assignment, a is treated as local to the function b and when a = a + 2 is encountered, Python knows a is a local variable, no confusion is present here.
In the other case you added:
def b():
def c():
print c
return c()
You are only referencing a name enclosed in the scope of function b, referencing. If you changed this to a similar assignment as before, you'll get the same UnboundLocalError:
def b():
def c():
c = c + 20
return c()
Because you are doing an assignment and the scope has to be clarified.
Your case:
a = 3
def b():
a = a + 1
return a
print(b())
Output:
Traceback (most recent call last):
File "./tests/test1.py", line 12, in <module>
print(b())
File "./tests/test1.py", line 9, in b
a = a + 1
UnboundLocalError: local variable 'a' referenced before assignment
When you do the assignement a = ... you have created always a local variable unless it has been declared global which is not the case. Local resolution takes precedence.
Your example would obviously work with:
a = 3
def b():
global a
a = a + 1
return a
print(b())
Which outputs:
4
But using globals, imho, should not be the way and hence passing a as a parameter to function b would be it:
a = 3
def b(a):
a = a + 1
return a
print(b(a))
Which agains produces the expected 4.
EDIT:
Following the edition in the OP: you can always "reference" the global scope (and parent scope). But if you make assignments they take place in the local scope.
If your code (which is wronly indented and contains a new c which isnt' anywhere) is meant to do this:
a = 3
def b():
def c():
print(a)
return c()
b()
It works ... and prints 3 because there is NO assignment and only referencing. As soon as you executing an assignment a would again be created in the local scope.
Assignment will create a new variable in Python.
Here may help you.
Take this example:
def f():
myvar = None
def g():
print myvar
...
myvar = get_real_value()
g()
Is "myvar = None" a conventional (or at least, reasonable) way of declaring the variable, to make it visible to g()? Is there a better way? (Python 2.6.x, if relevant)
There is no need to declare the variable before the definition for g():
>>> def f():
... def g():
... print myvar
... myvar = 1
... g()
...
>>> f()
1
However, if you can avoid referencing non-local variables in g() that would be preferable, which you would probably do here by having myvar be a parameter to g().
You don't need to pre-initialize the variable; it only has to be initialized before you call g(). Just remove the myvar = None line.
For clarity, I prefer to move local functions as close to their point of invocation as practicable, so that the initialisation of local variables that a function uses comes before the function itself.