Migrating from C++, Does Python do call by reference or call by?value?

Migrating from C++, Does Python do call by reference or call by?value?

If you are migrating to python from C++, you will often come across this question about how are variables passed inside a python function. Is it pass-by-reference or pass-by-value? The answer to this question is pass by object reference, which is passed by value.

Confusing isn’t it?!!! Let me make it clear to you

Let me get to the point by using a simple example.

Let’s say that you have a list a = [1,2,3] you need to change the list to a =[43,34,55] at the same address, inside a function, without using return, how can you solve it?

Experiment 1:

a  =[1,2,3]
print("original a:",a)
def change_list(l:list):
    l = [43,34,55]
    return l
    
change_list(a)
print("changed a:",a)        

Output 1:

original a: [1, 2, 3]

changed a: [1, 2, 3]

Nothing has changed, everything remains the same throughout.

In python, variables are passed by reference but the reference is passed by value. This means that the reference you have inside change_list() is actually a copy of the reference you passed. This again implies that if you reassign the variable inside the function. It is a local variable existing only inside the function; re-assigning it won’t change anything in outside scopes.

Let us look at this experiment

Now, we will use a mutable operation such as [:]

a  =[1,2,3]
def change_list(l:list):
    l[:] = [43,34,55]
    
    print("id of l:",id(l))
    return l

print(a)
print("id of a:",id(a))
change_list(a)
print("id of a:",id(a))
print(a)        

Output?:

[1, 2, 3]

id of a: 140600686866048

id of l: 140600686866048

id of a: 140600686866048

[43, 34, 55]

Hurry we have successfully changed our list members to new members, so now we have asserted that given a list l if we use l[:] = [43,34,55] will change the list that we pass through our function in the same reference as we expect.

Let’s talk about using the slicing operator “:” on the left side of the assignment operator, in this case, we create a shallow copy of the list that creates a different reference and therefore a different object. But be extremely careful when using this operator for 2 or higher dimensions of list as it will create two new references with shared lists. Examples are given in the conclusion.

Conclusion

  1. In the case of mutable python objects such as list, Set, and Dictionary, changes inside a function using append, insert, extend,…methods will reflect the changes outside the function for the same object but the assignment operator will not lead to this change. However, in the case of immutable types such as tuples, int, float, bool, frozenset, there is no way to change them inside a function without using return or wrapper functions. Regarding the use of “:”, the slicing operator before the assignment operator works for 1d list but in the case of multidimensional lists, please refer to point no 2. in conclusion
  2. Using “:” on the right side of the “=” operator is done for shallow copying, in creating new references but for list = list[:] for 2-dimensional and 3-dimensional lists be extra careful as in this case shallow copy does not work and operations such as changing element get reflected on both the lists with old as well as new references.

Example

One Dimensional List

  a  =[1,2,3]
  print("original a: ",a)
  b = a[:] # creates two address it is a shallow copy
  
  a[0] = 25
  b[1] = 55
  
  print("changed a:",a)
  print("changed b:",b)
  print(a is b)        

OUTPUT:

original a: [1, 2, 3]

changed a: [25, 2, 3]

changed b: [1, 55, 3]

False

Shallow copy worked

a and b are changed and reflected changes respectively and separately.

But in the case of 2d lists, see the example below

Example when using “:”

a  =[[1],[2],[3]]
print("original a:",a)

b = a[:]

a[0][0] = 25
b[1][0] = 55

print("changed a: ",a)
print("changed b: ",b)
print(a is b)
print(a[0] is b[0])        

OUTPUT:

original a: [[1], [2], [3]]

changed a: [[25], [55], [3]]

changed b: [[25], [55], [3]]

False

True

a and b are changed but as lists are copied from a to b, the list copied are shared, and therefore we see False because a is not b but True when the list inside a that is a[0] is the same as the list inside b which is b[0]

Example when not using “:”

a  =[[1],[2],[3]]
print("original a:",a)

b = [[j for j in i] for i in a] 

a[0][0] = 25
b[1][0] = 55

print("changed a: ",a)
print("changed b: ",b)
print(a is b)
print(a[0] is b[0])        

OUTPUT:

original a: [[1], [2], [3]]

changed a: [[25], [55], [3]]

changed b: [[25], [55], [3]]

True

True

As we see everything is true, a and b share the same resource including their lists inside completely.

Here the question is how then can we create two different entities in the case of 2d and 3d lists the answer to this problem is as follows:

a  =[[1],[2],[3]]
print("original a:",a)

b = [[j for j in i] for i in a] 

a[0][0] = 25
b[1][0] = 55

print("changed a: ",a)
print("changed b: ",b)
print(a is b)
print(a[0] is b[0])        

OUTPUT:

original a: [[1], [2], [3]]

changed a: [[25], [2], [3]]

changed b: [[1], [55], [3]]

False

False

In this example we see everything is false, now we have successfully created two different entities a and b, also known as deep copy.

References:

https://stackoverflow.com/questions/986006/how-do-i-pass-a-variable-by-reference

https://stackoverflow.com/questions/22054698/python-modifying-list-inside-a-function/22055029

https://stackoverflow.com/questions/4081561/what-is-the-difference-between-list-and-list-in-python

https://robertheaton.com/2014/02/09/pythons-pass-by-object-reference-as-explained-by-philip-k-dick/

https://www.geeksforgeeks.org/pass-by-reference-vs-value-in-python/

https://towardsdatascience.com/understanding-reference-counting-in-python-3894b71b5611

要查看或添加评论,请登录

Neil Pradhan的更多文章

其他会员也浏览了