1

Many articles on Internet say that python parameters are passed by reference. But from this snippet, the variable d is unchanged after the function test() is called. It is different from C/C++. Could someone please explain it? Thanks.

def test(_d : dict):
    _d = dict()
    _d.update({'D': 4})
    print("Inside the function",_d)
    return

d = {'A': 1,'B':2,'C':3}
test(d)
print("outside the function:", d) # expected: {'D:4}
Tuan Le PN
  • 364
  • 1
  • 12
  • I think [this](https://stackoverflow.com/questions/986006/how-do-i-pass-a-variable-by-reference) might help answer your question. – idjaw Jun 19 '21 at 02:01
  • ANd [this](https://stackoverflow.com/questions/4557223/python-basic-data-references-list-of-same-reference) – dawg Jun 19 '21 at 02:06
  • Print the [id](https://docs.python.org/3/library/functions.html#id) of each `d` or `_d` When you have `_d = dict()` you have just replaced what the name refers to... – dawg Jun 19 '21 at 02:07
  • 1
    See https://nedbatchelder.com/text/names.html – chepner Jun 19 '21 at 02:10
  • Variables in Python are like pointers in C/C++. In C/C++ variables refer to storage and a type for the value stored there... but in Python variables refer to values, that have types and storage.. the types (at least mutable types) are are independent of the variables that refer to them... In Python all variables are all referred to by reference. – 9mjb Jun 19 '21 at 03:18

1 Answers1

1

Nothing complicated here.

When you have _d = dict() you just created a new name _d (local to the function test) for a new dict and lost the local name _d referring to the global dict d that passed to that function as _d initially:

def test(_d : dict):
    print("Passed _d id:",id(_d))
    _d = dict()
    print("new _d id:",id(_d))
    _d.update({'D': 4})
    print("Inside the function",_d)
    return

d = {'A': 1,'B':2,'C':3}
print('Passed d id:',id(d))
test(d)
print("outside the function:", d) # expected: {'A':1, 'B':2, 'C':3, 'D:4}

Prints:

Passed d id: 4404101632
Passed _d id: 4404101632
new _d id: 4405135680
Inside the function {'D': 4}
outside the function: {'A': 1, 'B': 2, 'C': 3}

Now try:

def test(_d : dict):
    print("Passed _d id:",id(_d))
    # _d = dict()
    print("new _d id?:",id(_d))
    _d.update({'D': 4})
    print("Inside the function",_d)
    return

d = {'A': 1,'B':2,'C':3}
print('Passed d id:',id(d))
test(d)
print("outside the function:", d) # expected: {'A':1, 'B':2, 'C':3, 'D:4}

Prints:

Passed d id: 4320473600
Passed _d id: 4320473600
new _d id?: 4320473600
Inside the function {'A': 1, 'B': 2, 'C': 3, 'D': 4}
outside the function: {'A': 1, 'B': 2, 'C': 3, 'D': 4}

Note in the second case the id is the same at all three places printed so it is the same object; in the first case, the id changes so you have a different object after _d = dict() is called.


Note:

Since d is global and mutable, this works as expected as well:

def test():
    d.update({'D': 4})

d = {'A': 1,'B':2,'C':3}
test()
print(d) # {'A': 1, 'B': 2, 'C': 3, 'D': 4}
dawg
  • 98,345
  • 23
  • 131
  • 206
  • "When you have _d = dict() you just created a new name _d (local to the function test) for a new dict and lost the local name _d referring to the global dict d..." I think the local name _d is not lost, it is still there, and it is still the first parameter's name. After, we assign _d = dict(), it refer to the newly created object not the original dict object any more. So, the changes we made will go to the new object, not the global object. This explain why the global object is unchanged. – Tuan Le PN Jun 19 '21 at 05:36
  • As I come from C/C++ world, I think this is similar to "passed by pointer in C/C++" as we can reassign the pointer, it is not a "C++ passed-by-reference". Anyway, there is no "real pointer" in python. Anyway, your code helps to clear my miss-understanding. – Tuan Le PN Jun 19 '21 at 05:37
  • *I think the local name _d is not lost, it is still there, and it is still the first parameter's name.* No, it really is lost. The function has its own namespace created on entry into the function. One element is the parameter `_d` in that local namespace. The action of calling `_d = dict()` replaced that entry and it is lost locally. It is still available in the enclosing namespace but you would need to know how to access that. It is no longer universally available. See [scope and namespace](https://docs.python.org/3/tutorial/classes.html#python-scopes-and-namespaces) – dawg Jun 19 '21 at 14:30