0
d = {'a':'1', 'b':'2', 'c':'3'}

def change_d(d):
    dd = {}
    d = dd
    print(d)

change_d(d)
print(d)

I suppose both print() should print out an empty dict, but they are different:

{}
{'a': '1', 'b': '2', 'c': '3'}

Why doesn't the assignment in the function change the original dict's value outside of the function?

marlon
  • 6,029
  • 8
  • 42
  • 76
  • Because **assignment never mutates**. `d = dd` does not say, "the object being referred to by `d` is now mutated to have the same value as the object referred to by `dd`", it means, "the local variable `d` now refers to the object being referred to by `dd`". – juanpa.arrivillaga Jul 21 '21 at 19:23
  • So in short, because **your `change_d` function doesn't mutate the object you pass as a parameter anywhere**. – juanpa.arrivillaga Jul 21 '21 at 19:23
  • In order to make a copy of your dictionary. Try this: dd = d.copy() – TheHappyBee Jul 21 '21 at 19:24
  • I highly suggest reading: https://nedbatchelder.com/text/names.html – juanpa.arrivillaga Jul 21 '21 at 19:24
  • @TheHappyBee why make a copy? That wouldn't give the OP the expected behavior, and would do nothing useful as far as I can tell – juanpa.arrivillaga Jul 21 '21 at 19:25
  • @juanpa.arrivillaga So assignment in Python creates a new variable, rather than modify the original variable's value. Is it different from other languages? – marlon Jul 21 '21 at 19:25
  • Deleted my original comment; the issue here is about why the global variable `d` isn't changed by an assignment to the local variable `d`. – chepner Jul 21 '21 at 19:25
  • @marlon Read the link. No, it doesn't necessarily create a "new variable". Stop thinking in terms of C variables. Python is not C. Python is a high-level, object oriented language. Assigning to a name simply makes a that name refer to whatever object you assign to it. Just because that name was referring to another object won't make it affect that other object. The semantics of this are the same in Python, Java, Javascript, Ruby etc etc – juanpa.arrivillaga Jul 21 '21 at 19:26
  • The simple solution is to have the body of the function call `d.clear()`. That's all that it needs to do. – Samwise Jul 21 '21 at 19:28
  • 1
    @Samwise right, the point being for the OP, if you want to mutate the object, then you need to use *mutator methods on the object*. – juanpa.arrivillaga Jul 21 '21 at 19:30
  • 1
    It's odd to me that you have been programming in languages with these semantics (Java, Python) for at least four years, but haven't noticed until now. (The only mainstream languages that supports the semantics you expect is C++, but only with reference types.) – molbdnilo Jul 21 '21 at 19:33
  • @molbdnilo C# as well. – juanpa.arrivillaga Jul 21 '21 at 19:36
  • @juanpa.arrivillaga Ah, yes, I forgot about that – and I even program in it sometimes. Shows how often I actually care about call by reference these days, I guess. – molbdnilo Jul 21 '21 at 19:54
  • @molbdnilo IMO it is almost always a bad design choice. Functions that mutate their inputs are bad enough, but functions that modify the local variables *in the caller* ? Madness. I suppose it makes sense as an optimization in a low-level language like Fortran in a performance critical scenario. Otherwise, I'd avoid it like a plague – juanpa.arrivillaga Jul 21 '21 at 20:15

1 Answers1

1

There are two variables named d here. One in the global scope, the other in the local scope of change_d, due to the name of the function's parameter.

If you really want to modify what the global variable refers to, you need to do two things:

  1. Change the name of the parameter so that you don't shadow the global. (Really, you don't need a parameter at all, because you aren't going to use it.)
  2. Use global d to prevent d = ... from creating a new local variable.

Thus,

d = {'a':'1', 'b':'2', 'c':'3'}


def change_d():
    global d
    d = {}

print(d)
change_d()
print(d)
chepner
  • 497,756
  • 71
  • 530
  • 681
  • I don't actually think this is what they OP wants either, they seem to want something like call by reference, which as we all know, Python never supports. – juanpa.arrivillaga Jul 21 '21 at 19:29
  • My original problem is that, the 'd' passed into the function is from another function, not in the global scope. I think I need to use return values from the function, not modifying the parameter's values. – marlon Jul 21 '21 at 19:29
  • @juanpa.arrivillaga Yes, modify by reference. I think I got the solution by 'return' modified parameters. – marlon Jul 21 '21 at 19:30
  • @marlon well, your function **could** mutate the dict, returning doesn't actually do that. Usually that is bad design though, but that is possible. It is usually best to use pure functions whenever possible – juanpa.arrivillaga Jul 21 '21 at 19:30
  • @juanpa.arrivillaga The question never asks for any specific result, just why assignment to `d` had a particular effect. – chepner Jul 21 '21 at 19:32
  • Inside the function, I have procedures to create a new dict whose values are supposed to replace the original dict's values, so I thought the easiest way would be to do a simple assignment to replace values. This won't work, so I can just return the new dict and the caller will get the modified dict this way. – marlon Jul 21 '21 at 19:39