Global variable's value lost between function calls in python - python

I know I must have missed something basic -- just want to make sure I get the precise answer.
I have the following code. Why CACHE_KEYS is still None after load() while CACHE is not?
import bisect
import csv
DB_FILE = "GeoLiteCity-Location.csv"
# ['locId', 'country', 'region', 'city', 'postalCode', 'latitude', 'longitude', 'metroCode', 'areaCode']
CACHE = []
CACHE_KEYS = None
def load():
R = csv.reader(open(DB_FILE))
for line in R:
CACHE.append(line)
# sort by city
CACHE.sort(key=lambda x: x[3])
CACHE_KEYS = [x[3] for x in CACHE]
if __name__ == "__main__":
load()
# test
# print get_geo("Ruther Glen")

I think making it global would work. Any variable defined in the global scope which is to be edited should be specified as global in that function. Your code just makes a local variable CACHE_KEYS and stores the information correctly. But, to make sure that it is copied to the global variable, declare the variable as global in the function. You call the append function on CACHE and hence that works fine. Your code after the changes.
import bisect
import csv
DB_FILE = "GeoLiteCity-Location.csv"
# ['locId', 'country', 'region', 'city', 'postalCode', 'latitude', 'longitude', 'metroCode', 'areaCode']
CACHE = []
CACHE_KEYS = None
def load():
R = csv.reader(open(DB_FILE))
for line in R:
CACHE.append(line)
# sort by city
CACHE.sort(key=lambda x: x[3])
global CACHE_KEYS
CACHE_KEYS = [x[3] for x in CACHE]
if __name__ == "__main__":
load()
Anytime you assign a value to a global variable you have to declare it as global. See the following code snippet.
listOne = []
def load():
listOne+=[2]
if __name__=="__main__":
load()
The above code has an assignment and not an append call. This gives the following error.
UnboundLocalError: local variable 'listOne' referenced before assignment
But, executing the following snippet.
listOne = []
def load():
listOne.append(2)
if __name__=="__main__":
load()
gives the following output.
>>> print listOne
[2]

The thing is in
CACHE_KEYS = [x[3] for x in CACHE]
CACHE_KEYS is defined in the global scope. In the above line, you are assigning it a new value, bringing it into the local scope, In order to manipulate the variable in a function (and keep it's value later), global it :
def load():
global CACHE_KEYS
...
CACHE.sort(key=lambda x: x[3])
CACHE_KEYS = [x[3] for x in CACHE]

Related

Global variable not defined even though it appears in globals()

I wrote this code:
def openFile():
f = open("test.txt", "r")
mainInput = f.read()
global tupleMain
tupleMain = [tuple(mainInput.split(" ")) for mainInput in mainInput.strip(",").split("\n")]
As you can see, I have defined tupleMain as a global variable, but when I try to use it outside the function, I get:
NameError: name 'tupleMain' is not defined
If I run:
is_global = "tupleMain" in globals()
print(is_global)
The output is:
True
I just don't get why it says it's not defined if it's in globals() and have set it to global.
Thanks in advance
EDIT: I use the variable in the following function:
def tableFunction():
fname = [x[2] for x in tupleMain]
sname = [x[3] for x in tupleMain]
position = [x[1] for x in tupleMain]
salary = [x[4] for x in tupleMain]
team = [x[0] for x in tupleMain]
playerTable = PrettyTable()
playerTable.field_names= ["Surname", "First Name", "Salary", "Position", "Team"]
for x in tupleMain:
playerTable.add_row([x[3], x[2], x[4], x[1], x[0]])
print(playerTable)
You never called the function before using the global variable it declares in some other function, so the code inside the function which declares the variable as global never got executed. You need to at-least execute or call the function before referencing the global variable.
Either call the function before you use the global variable elsewhere or define the global variable at the module level inside your code.

How to access the nonlocal variable in enclosing function from inner function if inner function already has its variable with the same name Python

I need to find the way to reference variable x = "Nonlocal" from inner_function_nonlocal(). Probably, the way how I referenced the x = "Global": globals()['x'], but I need your help with that one, please!
Please note: I can NOT comment or delete x = "Local" in order to write nonlocal x instead of it.
x = "Global"
def enclosing_funcion():
x = "Nonlocal"
def inner_function_global():
x = "Local"
print(globals()['x']) # Call the global a
def inner_function_nonlocal():
x = "Local" # <- This line can NOT be commented!!!
print(_?_?_?_?_) # <- What should I specify here in order to print x which is nonlocal?
inner_function_global()
inner_function_nonlocal()
if __name__ == '__main__':
enclosing_funcion()
output should be:
Global (this is already achieved)
Nonlocal (need help to get this one)
You can add a method to get at the Nonlocal value:
x = "Global"
def enclosing_funcion():
x = "Nonlocal"
def non_local():
return x
def inner_function_global():
x = "Local"
print(globals()['x']) # Call the global a
def inner_function_nonlocal():
x = "Local" # <- This line can NOT be commented!!!
print(non_local()) # <- What should I specify here in order to print x which is nonlocal?
inner_function_global()
inner_function_nonlocal()
if __name__ == '__main__':
enclosing_funcion()
Result:
Global
Nonlocal

New to using functions in Python

I am trying to understand why newNameList is not defined:
ListofNames1 = ['Mark', 'Andrew']
ListofNames2 = ['Anjela', 'Lora']
names = ListofNames1
def greeting(names):
newNameList = []
for item in names:
newNameList.append(str(names))
return (names)
print(greeting(names))
def function2(newNameList):
for each in newNameList:
newNameList2.append(newNameList.upper())
return (newNameList2)
print(function2(newNameList))
The output
['Mark', 'Andrew']
...
NameError: name 'newNameList' is not defined.
The name error occurs on the last line in the code.
newNameList is only defined within the scope of function2. Since the print statement is not indented at the same level of function2 then newNameList is not visible to it. The three variables defined at a top-level scope are ListofNames1, ListofNames1, and names. These are the only three variables that can be passed to function2 in the print statement.
Yes, You can do it.
For example:
def use_greeting_function(name):
new_list_name = greeting(name)
Now new_list_name has the output of greeting function and you can use it in the function afterwards.
NameError: name 'newNameList' is not defined.
tells you what's wrong. You should have defined newNameList outside the greetings() function.
I have rewritten your code:
ListofNames1 = ['Mark', 'Andrew']
ListofNames2 = ['Anjela', 'Lora']
names = ListofNames1
newNameList = []
def greeting(names):
for item in names:
newNameList.append(names)
return names
print(greeting(names))
def function2(newNameList):
newNameList2 = []
for each in newNameList:
newNameList2.append(str(newNameList).upper())
return newNameList2
print(function2(newNameList))
And using the upper() method on a list doesn't work. Convert it to str first.

Initialize variables when they're not already declared in python

I'm trying to use a simple try statement and a for loop statement to initialize a list of variables that were not defined previously.
Here's the code I wrote:
for i in ['var1', 'var2', 'var3']:
try:
i
except NameError:
i = []
It doesn't work as I expect it to. After running it, I want to have var1 = [], var2=[] and var3=[] if these variables haven't been defined previously.
Here' a little more detail on what I'm trying to accomplish. A scheduled task is supposed to run every 60 seconds and I want to keep track of progress:
def run_schduled():
for i in ['var1', 'var2', 'var3']:
try:
i
except NameError:
i = []
var1.append(random.randint(0,100))
var2.append(random.randint(0,100))
var3.append(random.randint(0,100))
schedule.every(60).seconds.do(run_schduled)
while True:
schedule.run_pending()
time.sleep(30)
One solution is to use a defaultdict:
from collections import defaultdict
my_dict = defaultdict(lambda: [])
my_dict['var1'].append(1)
print(my_dict['var1']) # prints '[1]'
This would not allow you to simply do print(var1), however, because it would still be undefined in your local (or global) namespace as a tagged value. It would only exist in the defaultdict instance as key.
Another option would be to use a class:
class TaskRunner:
def __init__(self, var1=None, var2=None, var3=None):
self.var1 = var1 or []
self.var2 = var2 or []
self.var3 = var3 or []
def run_scheduled(self):
for i in [self.var1, self.var2, self.var3]:
i.append(random.randrange(1, 10000000))
runner = TaskRunner()
schedule.every(60).seconds.do(runner.run_scheduled)
You can also use pickle to save instances to load later (i.e., in subsequent runs of your job).
Try globals:
In [82]: for i in ['var1', 'var2', 'var3']:
...: if i in globals():
...: print(f'{i} already present in the global namespace')
...: else:
...: globals()[i] = []
...:
In [83]: var1
Out[83]: []

How do you initialize a global variable only when its not defined?

I have a global dictionary variable that will be used in a function that gets called multiple times. I don't have control of when the function is called, or a scope outside of the function I'm writing. I need to initialize the variable only if its not initialized. Once initialized, I will add values to it.
global dict
if dict is None:
dict = {}
dict[lldb.thread.GetThreadID()] = dict[lldb.thread.GetThreadID()] + 1
Unfortunately, I get
NameError: global name 'dict' is not defined
I understand that I should define the variable, but since this code is called multiple times, by just saying dict = {} I would be RE-defining the variable every time the code is called, unless I can somehow check if it's not defined, and only define it then.
Catching the error:
try:
_ = myDict
except NameError:
global myDict
myDict = {}
IMPORTANT NOTE:
Do NOT use dict or any other built-in type as a variable name.
A more idiomatic way to do this is to set the name ahead of time to a sentinel value and then check against that:
_my_dict = None
...
def increment_thing():
global _my_dict
if _my_dict is None:
_my_dict = {}
thread_id = lldb.thread.GetThreadID()
_my_dict[thread_id] = _my_dict.get(thread_id, 0) + 1
Note, I don't know anything about lldb -- but if it is using python threads, you might be better off using a threading.local:
import threading
# Thread local storage
_tls = threading.local()
def increment_thing():
counter = getattr(_tls, 'counter', 0)
_tls.counter = counter + 1

Categories