it seems that strings and dicts behave fundamentally differently in python. when i pass a string to a function it gets modified in the local function's scope only, but when i do the same with a dict, it gets modified in the scope beyond the function:
def change_str(s):
s += " qwe"
def change_arr(a):
a[1] = "qwe"
ss = "asd"
change_str(ss)
print ss
# prints:
# asd
aa = {0:"asd"}
change_arr(aa)
print aa
# prints:
# {0: 'asd', 1: 'qwe'}
is this behavior intentional, and if so then why?
3 Answers 3
It is intentional behavior. Strings are immutable in python, so essentially all string operations return a new string and as your functions do not return anything, you cannot see the new string asd qwe. You can change the contents of mutable containers outside of local scope without declaring them global.
You can read more about mutable types in the official documentation of pythons data model.
4 Comments
a_new = a.copy() then modify a_new, but just wondering if there is another waydict.copy() will not help you, because only the reference is copied. You'll have to use copy.deepcopy. However you should consider dropping the local scope all together, and design your code so you won't have to worry about such problems.Don't let the 'assignment' operator fool you. This is what is really going on in each of these functions:
def change_str(s):
# operation has been split into 2 steps for clarity
t = s.__iadd__("qwe") # creates a new string object
s = t # as you know, without the `global` keyword, this `s` is local.
def change_arr(a):
a.__setitem__(1, "qwe")
As you can see, only one of these functions actually has an assignment operation. The []= is shorthand for (or equivalent to) .__setitem__().
1 Comment
Yes, it's intentional. Each type determines how operators work on it. The dict type is set up so that a[1] = "qwe" modifies the dict object. Such changes will be seen in any piece of code that references that object. The string type is set up so that s += "qwe" does not modify the object, but returns a new object. So other code that was referencing the original object will see no changes.
The shorthand way of saying that is that strings are immutable and dicts are mutable. However, it's worth noting that "dicts are mutable" isn't the whole reason why the behavior happens. The reason is that item assignment (someDict[item] = val) is an operation that actaully mutates a dict.
+=and[]=are operations that could be done in place on some types, just not these particular types. That other question compares item assignment to bare-name assignment, which can never mutate.