Numeric variable scope in Python closure (Python v2.5.2) - python

I have a nested function where I am trying to access variables assigned in the parent scope. From the first line of the next() function I can see that path, and nodes_done are assigned as expected. distance, current, and probability_left have no value and are causing a NameError to be thrown.
What am I doing wrong here? How can I access and modify the values of current, distance, and probability_left from the next() function?
def cheapest_path(self):
path = []
current = 0
distance = 0
nodes_done = [False for _ in range(len(self.graph.nodes))]
probability_left = sum(self.graph.probabilities)
def next(dest):
log('next: %s -> %s distance(%.2f), nodes_done(%s), probability_left(%.2f)' % (distance,self.graph.nodes[current],self.graph.nodes[dest],str(nodes_done),probability_left))
path.append((current, distance, nodes_done, probability_left))
probability_left -= self.graph.probabilities[current]
nodes_done[current] = True
distance = self.graph.shortest_path[current][dest]
current = dest
def back():
current,nodes_done,probability_left = path.pop()

The way Python's nested scopes work, you can never assign to a variable in the parent scope, unless it's global (via the global keyword). This changes in Python 3 (with the addition of nonlocal), but with 2.x you're stuck.
Instead, you have to sort of work around this by using a datatype which is stored by reference:
def A():
foo = [1]
def B():
foo[0] = 2 # since foo is a list, modifying it here modifies the referenced list
Note that this is why your list variables work - they're stored by reference, and thus modifying the list modifies the original referenced list. If you tried to do something like path = [] inside your nested function, it wouldn't work because that would be actually assigning to path (which Python would interpret as creating a new local variable path inside the nested function that shadows the parent's path).
One option that is sometimes used is to just keep all of the things that you want to persist down into the nested scope in a dict:
def A():
state = {
'path': [],
'current': 0,
# ...
}
def B():
state['current'] = 3

The short answer is that python does not have proper lexical scoping support. If it did, there would have to be more syntax to support the behavior (i.e. a var/def/my keyword to declare the variable scope).
Barring actual lexical scoping, the best you can do is store the data in an environment data structure. One simple example would be a list, e.g.:
def cheapest_path(self):
path = []
path_info = [0, 0]
nodes_done = [False for _ in range(len(self.graph.nodes))]
probability_left = sum(self.graph.probabilities)
def next(dest):
distance, current = path_info
log('next: %s -> %s distance(%.2f), nodes_done(%s), probability_left(%.2f)' % (distance,self.graph.nodes[current],self.graph.nodes[dest],str(nodes_done),probability_left))
path.append((current, distance, nodes_done, probability_left))
probability_left -= self.graph.probabilities[current]
nodes_done[current] = True
path_info[0] = self.graph.shortest_path[current][dest]
path_info[1] = dest
def back():
current,nodes_done,probability_left = path.pop()
You can do this or do inspect magic. For more history on this read this thread.

If you happen to be working with Python 3, you can use the nonlocal statement (documentation) to make those variables exist in the current scope, e.g.:
def next(dest):
nonlocal distance, current, probability_left
...

Related

Accessing python variable outside function scope when reassigning to update variable

I want to keep track of the current max of a calculated cosine similarity score. However, I keep getting the error UnboundLocalError: cannot access local variable 'current_max_cosine_similarity_score' where it is not associated with a value
In Javascript, I can typically do this without a problem using the let keyword when working with a variable outside of a function scope. However, in Python that doesn't seem to be the case.
What would be the pythonic way of going about this?
current_max_cosine_similarity_score = -math.inf
def func(acc, v):
calculated_cosine_similarity_score = ...
if calculated_cosine_similarity_score > current_max_cosine_similarity_score:
current_max_cosine_similarity_score = max([current_max_cosine_similarity_score, calculated_cosine_similarity_score])
acc['cosineSimilarityScore'] = calculated_cosine_similarity_score
return acc
print(reduce(func, [...], {}))
You have to declare current_max_cosine_similarity_score as global (or nonlocal) in func().
But that's nevertheless a bad idea. The "pythonic" way would be to use a generator, closure or a class with a get_current_maximum().
Probably the most "pythonic" closure solves your problem:
from functools import reduce
def calc_closure():
def _calc(value, element):
# do calculations on element here
if element > value:
_calc.current_max_value = element
return _calc.current_max_value
# using an attribute makes current_max_value accessible from outer
_calc.current_max_value = -np.math.inf
return _calc
closure_1 = calc_closure()
closure_2 = calc_closure()
print(reduce(closure_1, [1, 2, 3, 4, 1]))
print(closure_1.current_max_value )
print(closure_2.current_max_value )
Output:
4
4
-inf

Get a Variable directly from function Python

i want to work with the variable "sets" after the function has been called.
How can i do this?
sets = 0
def search_str(monatsabrechnung, d1):
with open(monatsabrechnung, 'r') as file:
content = file.read()
if lastDay == heute and ja == 1:
sets = 1
else:
pass
search_str(monatsabrechnung, d1)
print(sets)
IIUC, you are trying to modify a variable in a function, which is originally defined outside a function. This is a variable scoping problem. Do check this awesome article to get an understanding of how variable scopes work in python.
Back to your code, the issue here is that even though you run the function to modify the variable sets to 1, python goes back to the outer scope where sets were set to 0.
sets = 0 #<-- outer scope
def search_str():
... #do something
sets = 1 #<-- inner scope
search_str()
print(sets) #back to outer scope
# 0
Solution 1: Pass as a parameter and return
You will have to pass the variable as a parameter to the function and then return it as follows -
sets = 0
def search_str(sets): #<-- pass as paramter
... #do something
sets = 1 #<-- modify
return sets
output = search_str(sets) #<-- save the output
print(output)
# 1
TIP: If your function is already returning another output, you can actually return, store and unpack multiple return values at once -
In your return statement, return everything you need return x, y, sets
Then, while calling use - X, Y, sets = search_str(...)
Solution 2: Set scope to global
If passing a parameter and returning is not an option for you, the scope of the variable has to be made global -
sets = 0
def search_str():
global sets #<-- set the scope to global
... #do something
sets = 1
search_str()
print(sets)
# 1
EDIT: As an additional pointer, as Matthias points out in his comments correctly, global scoping is usually avoided as it can cause a lot of problems if not careful.
Here is a great StackOverflow thread detailing Why are global variables evil?
Return the new value of sets from the function. If the if statement is true, then return 1, otherwise return the same value as before. To be able to return the old value you should add it as a parameter to the function.
sets = 0
def search_str(monatsabrechnung, d1, sets):
with open(monatsabrechnung, 'r') as file:
content = file.read()
if lastDay == heute and ja == 1:
current_sets = 1
else:
current_sets = sets
return current_sets
sets = search_str(monatsabrechnung, d1, sets)
Don't use global if you don't have to. You get side-effects and the code is less reusable.

how to return a global variable for access in different functions [duplicate]

This question already has answers here:
Using global variables in a function
(25 answers)
Closed 5 months ago.
I know I should avoid using global variables in the first place due to confusion like this, but if I were to use them, is the following a valid way to go about using them? (I am trying to call the global copy of a variable created in a separate function.)
x = "somevalue"
def func_A ():
global x
# Do things to x
return x
def func_B():
x = func_A()
# Do things
return x
func_A()
func_B()
Does the x that the second function uses have the same value of the global copy of x that func_a uses and modifies? When calling the functions after definition, does order matter?
If you want to simply access a global variable you just use its name. However to change its value you need to use the global keyword.
E.g.
global someVar
someVar = 55
This would change the value of the global variable to 55. Otherwise it would just assign 55 to a local variable.
The order of function definition listings doesn't matter (assuming they don't refer to each other in some way), the order they are called does.
Within a Python scope, any assignment to a variable not already declared within that scope creates a new local variable unless that variable is declared earlier in the function as referring to a globally scoped variable with the keyword global.
Let's look at a modified version of your pseudocode to see what happens:
# Here, we're creating a variable 'x', in the __main__ scope.
x = 'None!'
def func_A():
# The below declaration lets the function know that we
# mean the global 'x' when we refer to that variable, not
# any local one
global x
x = 'A'
return x
def func_B():
# Here, we are somewhat mislead. We're actually involving two different
# variables named 'x'. One is local to func_B, the other is global.
# By calling func_A(), we do two things: we're reassigning the value
# of the GLOBAL x as part of func_A, and then taking that same value
# since it's returned by func_A, and assigning it to a LOCAL variable
# named 'x'.
x = func_A() # look at this as: x_local = func_A()
# Here, we're assigning the value of 'B' to the LOCAL x.
x = 'B' # look at this as: x_local = 'B'
return x # look at this as: return x_local
In fact, you could rewrite all of func_B with the variable named x_local and it would work identically.
The order matters only as far as the order in which your functions do operations that change the value of the global x. Thus in our example, order doesn't matter, since func_B calls func_A. In this example, order does matter:
def a():
global foo
foo = 'A'
def b():
global foo
foo = 'B'
b()
a()
print foo
# prints 'A' because a() was the last function to modify 'foo'.
Note that global is only required to modify global objects. You can still access them from within a function without declaring global.
Thus, we have:
x = 5
def access_only():
return x
# This returns whatever the global value of 'x' is
def modify():
global x
x = 'modified'
return x
# This function makes the global 'x' equal to 'modified', and then returns that value
def create_locally():
x = 'local!'
return x
# This function creates a new local variable named 'x', and sets it as 'local',
# and returns that. The global 'x' is untouched.
Note the difference between create_locally and access_only -- access_only is accessing the global x despite not calling global, and even though create_locally doesn't use global either, it creates a local copy since it's assigning a value.
The confusion here is why you shouldn't use global variables.
You can directly access a global variable inside a function. If you want to change the value of that global variable, use "global variable_name". See the following example:
var = 1
def global_var_change():
global var
var = "value changed"
global_var_change() #call the function for changes
print var
Generally speaking, this is not a good programming practice. By breaking namespace logic, code can become difficult to understand and debug.
As others have noted, you need to declare a variable global in a function when you want that function to be able to modify the global variable. If you only want to access it, then you don't need global.
To go into a bit more detail on that, what "modify" means is this: if you want to re-bind the global name so it points to a different object, the name must be declared global in the function.
Many operations that modify (mutate) an object do not re-bind the global name to point to a different object, and so they are all valid without declaring the name global in the function.
d = {}
l = []
o = type("object", (object,), {})()
def valid(): # these are all valid without declaring any names global!
d[0] = 1 # changes what's in d, but d still points to the same object
d[0] += 1 # ditto
d.clear() # ditto! d is now empty but it`s still the same object!
l.append(0) # l is still the same list but has an additional member
o.test = 1 # creating new attribute on o, but o is still the same object
Here is one case that caught me out, using a global as a default value of a parameter.
globVar = None # initialize value of global variable
def func(param = globVar): # use globVar as default value for param
print 'param =', param, 'globVar =', globVar # display values
def test():
global globVar
globVar = 42 # change value of global
func()
test()
=========
output: param = None, globVar = 42
I had expected param to have a value of 42. Surprise. Python 2.7 evaluated the value of globVar when it first parsed the function func. Changing the value of globVar did not affect the default value assigned to param. Delaying the evaluation, as in the following, worked as I needed it to.
def func(param = eval('globVar')): # this seems to work
print 'param =', param, 'globVar =', globVar # display values
Or, if you want to be safe,
def func(param = None)):
if param == None:
param = globVar
print 'param =', param, 'globVar =', globVar # display values
You must use the global declaration when you wish to alter the value assigned to a global variable.
You do not need it to read from a global variable. Note that calling a method on an object (even if it alters the data within that object) does not alter the value of the variable holding that object (absent reflective magic).

Using append() method on a global list in a function (Python)

I am having issues understanding how global variables can or cannot be edited within the local scope of functions, please see the two example codes I have provided which contrast eachother:
Example 1
def function():
x[0] = True
x = [False] # global variable
function()
print(x) # True
Example 2
def function():
z = True
z = False # global variable
function()
print(z) # False
I was of the understanding that global variables cannot be edited within a local scope (inside a function) unless they are explicitly called globally i.e (global "variable_name"), however example 1 indicates that when dealing with lists and using the append method, I can in-fact edit the real global variable without having to call it with the explicit global term. Where as in example 2, I am unable to re-assign the global variable which is contrary to example 1. Please explain why this is the case, does the append method or lists in particular interact with global/local scope rules differently?
Lists are mutable, so while the variable can not be edited (assigned to another list for example) the contents of the list may be modified and the variable will still point to the same list, so the variable is untouched.
Try:
def function():
x = [True]
x = [False] # global variable
function()
print(x)

How to access a list passed a parameter to a function inside the function?

How to access the list sent as a parameter to function inside the function?
def my_fun(mylist):
mylist[0]=a
mylist[1]=b
mylist[2]=c
mylist[3]=d
e=a+b
f=c+d
return (e,f))
val = [1,2,3,4]
my_fun(val)
NameError: name 'a' is not defined
You dont want to assign your list index to a variable (mylist[0] = a) but the other way around (a = mylist[0]), so that you can store the values when a list is passed as an argument.
def my_fun(mylist):
a = mylist[0]
b = mylist[1]
c = mylist[2]
d = mylist[3]
e=a+b
f=c+d
return (e,f)
def main():
val = [1,2,3,4]
x = my_fun(val)
print(x)
main()
What you are looking is globals() scope dictionary. Python has 2 functions locals() that return local variable dictionary inside the scope, and globals() that return global variables outside the scope. Both functions return a dict which change during execution. So you can change, check for change or even change them artificially during your code.
Let's say you have this type of code:
a = 4;
b = 3;
c = 5;
d = 2;
# version 1
def my_fun(mylist):
global a,b,c,d; # mentioning them as global variables
mylist[0]=a
mylist[1]=b
mylist[2]=c
mylist[3]=d
e=a+b
f=c+d
return (e,f)
By declaring global a,b,c,d you are placing them temporarily in global namespace. This way you can access the global variables from inside the function.
# or version 2
def my_fun2(mylist):
mylist[0]=globals()['a']
mylist[1]=globals()['b']
mylist[2]=globals()['c']
mylist[3]=globals()['d']
e=globals()['a']+globals()['b']
f=globals()['c']+globals()['d']
return (e,f)
By calling globals() and calling the variable name ['a'] you are accessing directly global namespace and as any dictionary you can change it by your will. But be carefull, changing variables starting with and ending __ (underscores) [ like __name__ can mess up your code, sometimes even interpreter. So keep track what you are changing and what you are changing it to.
#version 3
def my_fun3(mylist):
mylist[0]=globals()['a']
mylist[1]=globals()['b']
mylist[2]=globals()['c']
mylist[3]=globals()['d']
# changing of the global variables.
globals()['a'] = mylist[0]+mylist[1];
globals()['b'] = mylist[2]+mylist[3];
Be careful with this information, and test them in interpreter(IDE) first before committing to your code.
To get more information about function ( name, argument names, constants and etc ), you can do next:
>>def a():return 0
>>a.__code__ #grabs code object
>>a.__code__.co_name # gets the name of the function you can search
And more information on what you can call from __code__ object is dir(f_name.__code__) where f_name is a in this case.

Categories

Resources