Python Forum

Full Version: Variable scope - "global x" didn't work...
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
I am struggling to understand updating a 'global' dictionary (i.e., it's defined in the main module and I want to update it in a subroutine). I understand that I need to do something since dict_1 in the mainline isn't what would be updated in a function. So I tried this experiment:

dict_1 = {}
dict_2 = {}

def upd_dict1(dict_1_copy):
    print("sub dict_1_copy a is " + str(dict_1_copy["a"]))
    dict_1_copy["a"] = 100
    print("sub dict_1_copy a is " + str(dict_1_copy["a"]))
    return dict_1_copy

if __name__ == "__main__":
    dict_1["a"] = 1
    dict_1["b"] = 2
    dict_2["a"] = 0
    dict_2["b"] = 22
    print("dict_1 a is " + str(dict_1["a"]) + " b4 upd - sb 1")
    print("dict_1 b is " + str(dict_1["b"]) + " b4 upd - sb 2")
    print("dict_2 a is " + str(dict_2["a"]) + " b4 upd - sb 0")
    print("dict_2 b is " + str(dict_2["b"]) + " b4 upd - sb 22\n")
    dict_2 = upd_dict1(dict_1)
    print("dict_1 a is " + str(dict_1["a"]) + " aft upd - sb 1")
    print("dict_1 b is " + str(dict_1["b"]) + " aft upd - sb 2")
    print("dict_2 a is " + str(dict_2["a"]) + " aft upd - sb 100")
    print("dict_2 b is " + str(dict_2["b"]) + " aft upd - sb 2\n")
    dict_1 = dict_2
    print("dict_1 a is " + str(dict_1["a"]) + " aft copy - sb 100")
    print("dict_1 b is " + str(dict_1["b"]) + " aft copy - sb 2")
    print("dict_2 a is " + str(dict_2["a"]) + " aft copy - sb 100")
    print("dict_2 b is " + str(dict_2["b"]) + " aft copy - sb 2")
Which *should* give the results shown as "sb X" in the print statements. But what I get is

Output:
dict_1 a is 1 b4 upd - sb 1 dict_1 b is 2 b4 upd - sb 2 dict_2 a is 0 b4 upd - sb 0 dict_2 b is 22 b4 upd - sb 22 sub dict_1_copy a is 1 sub dict_1_copy a is 100 dict_1 a is 100 aft upd - sb 1 <-- THIS dict_1 b is 2 aft upd - sb 2 dict_2 a is 100 aft upd - sb 100 dict_2 b is 2 aft upd - sb 2 dict_1 a is 100 aft copy - sb 100 dict_1 b is 2 aft copy - sb 2 dict_2 a is 100 aft copy - sb 100 dict_2 b is 2 aft copy - sb 2
I completely do not understand why the line marked THIS get what it gets.

Can anyone tell me what I'm missing here?

Paul
You call it a copy, but there are no copies made anywhere. When you pass an object to a function, if that object is mutable the function can modify it.

Here you pass dict1 to upd_dict1 on line 19. This function both modifies the object that is passed in, and returns another reference to that object.

Before line 19 you have two dictionaries: dict_1 and dict_2. After line 19, both variables point at the same object (the original dict_2 is discarded). Line 24 doesn't do anything because they are already the same. You can add a statement like:

print(f"dict_1 and dict_2 are the same object: {dict_1 is dict_2}"
Normally you do not need to use "global" with dictionaries and lists. "global" controls how assignment is done. In the code below the "global x" in set_global_x() tells Python that the assignment "x = value" should not create a new x variable in the function scope. The same assignment in set_local_x() creates "x" inside the set_local_x namespace. Setting this x does not change the value of the "global" x.
x = 1

def set_global_x(value):
    global x
    x = value

def set_local_x(value):
    x = value

def print_x():
    print('x =', x)

print_x()
set_global_x(2)
print_x()
set_local_x(3)
print_x()
Output:
x = 1 x = 2 x = 2
When using a mutable object like a dictionary or list you don't need to use global if all you want to do is change something in the mutable object.
x = [1]

def set_global_x(value):
    global x
    x[0] = value

def set_local_x(value):
    x[0] = value

def print_x():
    print('x =', x[0])

print_x()
set_global_x(2)
print_x()
set_local_x(3)
print_x()
Output:
x = 1 x = 2 x = 3
(Dec-24-2020, 08:13 PM)bowlofred Wrote: [ -> ]You call it a copy, but there are no copies made anywhere. When you pass an object to a function, if that object is mutable the function can modify it.

Here you pass dict1 to upd_dict1 on line 19. This function both modifies the object that is passed in, and returns another reference to that object.

Before line 19 you have two dictionaries: dict_1 and dict_2. After line 19, both variables point at the same object (the original dict_2 is discarded). Line 24 doesn't do anything because they are already the same. You can add a statement like:

print(f"dict_1 and dict_2 are the same object: {dict_1 is dict_2}"

Thank you. The thing I am trying to fix is that when I do this in a third-level function:

for curr_id in dict_1: # loop through the dictionary
# code code code
    dict_1[curr_id].incl_ips.append(ipaddress.ip_network(rng_chop[0] + "/" + str(myprefix), False))
# code code code
it appends the value in the loop iteration to THE SAME dictionary entry.

Also, from my OP, essentially dict_1_copy is discarded when I exit the function and recreated the next time the function is called?
(Dec-25-2020, 12:00 AM)ptrivino Wrote: [ -> ]Thank you. The thing I am trying to fix is that when I do this in a third-level function:

for curr_id in dict_1: # loop through the dictionary
# code code code
    dict_1[curr_id].incl_ips.append(ipaddress.ip_network(rng_chop[0] + "/" + str(myprefix), False))
# code code code
it appends the value in the loop iteration to THE SAME dictionary entry.

I'm not sure I follow this part. What are you expecting it to append to?

Quote:Also, from my OP, essentially dict_1_copy is discarded when I exit the function and recreated the next time the function is called?

Yes. I wouldn't use "discarded" here because the actual data remains (since it has another name outside the function). I would say it is "reassigned" the next time the function is called. No data is created at that time.
(Dec-25-2020, 12:44 AM)bowlofred Wrote: [ -> ]
(Dec-25-2020, 12:00 AM)ptrivino Wrote: [ -> ]Thank you. The thing I am trying to fix is that when I do this in a third-level function:

for curr_id in dict_1: # loop through the dictionary
# code code code
    dict_1[curr_id].incl_ips.append(ipaddress.ip_network(rng_chop[0] + "/" + str(myprefix), False))
# code code code
it appends the value in the loop iteration to THE SAME dictionary entry.

I'm not sure I follow this part. What are you expecting it to append to?

Quote:Also, from my OP, essentially dict_1_copy is discarded when I exit the function and recreated the next time the function is called?

Yes. I wouldn't use "discarded" here because the actual data remains (since it has another name outside the function). I would say it is "reassigned" the next time the function is called. No data is created at that time.

In the dict_1 entries I set the value to an object/class with 4 attributes (incl_ips is one of them), each a list of other values. But what doesn't work is that, in each loop through with a new curr_id, the SAME dictionary entry (or possibly ALL entries, I've seen that in another try)is updated/appended to.