I have a dictionary that needs to be referred to by other functions. Rather than make it global, I pass the dictionary as an argument.
def functionA(dictionary):
dictionary = {}
functionB(dictionary)
print(f"In A, after B, length: {len(dictionary)}")
def functionB(dictionary):
dictionary["A"] = "some value"
dictionary["B"] = "another value"
def main():
dictionary = {}
functionA(dictionary)
print(f"Back in main. Length: {len(dictionary)}")
if __name__ == "__main__":
""" This is executed when run from the command line """
main()
In the above code, main()
initializes dictionary
as empty and passes it to functionA()
, which also (redundantly) sets it to empty and passes it to functionB()
, which adds a couple of entries.
What I expected: After main()
returns from functionA()
, I expected dictionary
to have the two entries added by functionB()
. However, instead, it was empty. The output was:
In A, after B, length: 2
Back in main. Length: 0
If, however, I remove the redundant dictionary = {}
in functionA
(i.e., the second line of the above code block), the problem goes away and I get the following output:
In A, after B, length: 2
Back in main. Length: 2
I don't see why the dictionary = {}
statement in functionA()
causes the subsequently added items to not get transferred back to main()
. Any insights? I feel like there's something fundamental here that I'm clueless about.
CodePudding user response:
functionA
reassigns the dictionary
name to a new piece of memory that is distinct from the memory referred to in the parameter.
Your code is logically equivalent to:
def functionA(dictionary):
dictionary_a = {}
functionB(dictionary_a)
print(f"In A, after B, length: {len(dictionary_a)}")
def functionB(dictionary):
dictionary["A"] = "some value"
dictionary["B"] = "another value"
def main():
dictionary = {}
functionA(dictionary)
print(f"Back in main. Length: {len(dictionary)}")
if __name__ == "__main__":
""" This is executed when run from the command line """
main()
which should make the issue more readily apparent.
You can think of the dictionary
in main()
and the dictionary
in functionA
as two separate references to the same dictionary. When dictionary
in functionA
is reassigned, it doesn't affect the value of dictionary
in main()
. As a rule of thumb, reassignment only affects the name that you put on the left-hand side of the statement.
CodePudding user response:
The critical thing is that dictionary = {}
does not empty the original dictionary. It instead creates a new dictionary. This is more easily seen if we bind the original dictionary to a different name:
def functionA(dictionary):
original = dictionary
dictionary = {}
functionB(dictionary)
print(f"In A, after B, length: {len(dictionary)}")
print(f"Original argument: {len(original)}")
In A, after B, length: 2
Original argument: 0
Back in main. Length: 0
The original dictionary that was created in main
and passed to functionA
is never modified, and so it is still empty. The dictionary
inside functionA
which was passed to functionB
is an entirely new object (separate from original
) that main
has no way of accessing because it was never return
ed.
If you want to modify the original dictionary by passing around references to it, you need to make sure that you use mutator methods instead of rebinding the name, e.g.:
def functionA(dictionary):
dictionary.clear()
functionB(dictionary)
print(f"In A, after B, length: {len(dictionary)}")
In A, after B, length: 2
Back in main. Length: 2
You can also avoid this type of confusion by always return
ing whatever data you want the caller to receive:
def functionA(dictionary):
dictionary = {}
dictionary = functionB(dictionary)
print(f"In A, after B, length: {len(dictionary)}")
return dictionary
def functionB(dictionary):
dictionary["A"] = "some value"
dictionary["B"] = "another value"
return dictionary
def main():
dictionary = {}
dictionary = functionA(dictionary)
print(f"Back in main. Length: {len(dictionary)}")
In A, after B, length: 2
Back in main. Length: 2