Function changes list values and not variable values in Python
Python variables contain pointers, or references, to objects. All values (even integers) are objects, and assignment changes the variable to point to a different object. It does not store a new value in the variable, it changes the variable to refer or point to a different object. For this reason many people say that Python doesn't have "variables," it has "names," and the =
operation doesn't "assign a value to a variable," but rather "binds a name to an object."
In plusOne
you are modifying (or "mutating") the contents of y
but never change what y
itself refers to. It stays pointing to the same list, the one you passed in to the function. The global variable y
and the local variable y
refer to the same list, so the changes are visible using either variable. Since you changed the contents of the object that was passed in, there is actually no reason to return y
(in fact, returning None
is what Python itself does for operations like this that modify a list "in place" -- values are returned by operations that create new objects rather than mutating existing ones).
In plusOne2
you are changing the local variable a
to refer to a different integer object, 3
. ("Binding the name a
to the object 3
.") The global variable a
is not changed by this and continues to point to 2
.
If you don't want to change a list passed in, make a copy of it and change that. Then your function should return the new list since it's one of those operations that creates a new object, and the new object will be lost if you don't return it. You can do this as the first line of the function: x = x[:]
for example (as others have pointed out). Or, if it might be useful to have the function called either way, you can have the caller pass in x[:]
if he wants a copy made.
Why is a list variable sometimes not impacted by changes in function as I thought python3 works on pass by reference with list variables?
Your ultimate problem is confusing (in-place) mutation with rebinding (also referred to somewhat less precisely as "reassignment").
In all the cases where the change isn't visible outside the function, you rebound the name inside the function. When you do:
name = val
it does not matter what used to be in name
; it's rebound to val
, and the reference to the old object is thrown away. When it's the last reference, this leads to the object being cleaned up; in your case, the argument used to alias an object also bound to a name in the caller, but after rebinding, that aliasing association is lost.
Aside for C/C++ folks: Rebinding is like assigning to a pointer variable, e.g. int *px = pfoo;
(initial binding), followed later by px = pbar;
(rebinding), where both pfoo
and pbar
are themselves pointers to int
. When the px = pbar;
assignment occurs, it doesn't matter that px
used to point to the same thing as pfoo
, it points to something new now, and following it up with *px = 1;
(mutation, not rebinding) only affects whatever pbar
points to, leaving the target of pfoo
unchanged.
By contrast, mutation doesn't break aliasing associations, so:
name[1] = val
does rebind name[1]
itself, but it doesn't rebind name
; it continues to refer to the same object as before, it just mutates that object in place, leaving all aliasing intact (so all names aliasing the same object see the result of the change).
For your specific case, you could change the "broken" functions from rebinding to aliasing by changing to slice assignment/deletion or other forms of in-place mutation, e.g.:
def func3(_in):
# _in = list() BAD, rebinds
_in.clear() # Good, method mutates in place
del _in[:] # Good, equivalent to clear
_in[:] = list() # Acceptable; needlessly creates empty list, but closest to original
# code, and has same effect
def func5_no_ret(_src, _dest1, _dest2):
# BAD, all rebinding to new lists, not changing contents of original lists
#_dest1 = [val for val in _src[0:len(_src):2]]
#_dest2 = [val for val in _src[1:len(_src):2]]
#_src = list()
# Acceptable (you should just use multiple return values, not modify caller arguments)
# this isn't C where multiple returns are a PITA
_dest1[:] = _src[::2] # Removed slice components where defaults equivalent
_dest2[:] = _src[1::2] # and dropped pointless listcomp; if _src might not be a list
# list(_src[::2]) is still better than no-op listcomp
_src.clear()
# Best (though clearing _src is still weird)
retval = _src[::2], _src[1::2]
_src.clear()
return retval
# Perhaps overly clever to avoid named temporary:
try:
return _src[::2], _src[1::2]
finally:
_src.clear()
Why can a function change an objects variable but not a variable
Python passes variables by Value, but objects by Reference.
So if you modify a variable, you modify your local copy, not the original; if you modify an object, you modify the original.
Changing the passed-in list within a function
Assigning to a name never changes the value previously referenced by the list. This is true even without invoking function parameters.
>>> x = [1,2,3]
>>> y = x
>>> y = [4,5,6]
>>> x
[1, 2, 3]
Changing what y
refers to does not alter what x
refers to.
In your second example, you are invoking a mutating method on the list referenced by the parameter. Both my_list
and in_list
refer to the same list, so it any changes made to the list via either reference are visible from either reference.
See https://nedbatchelder.com/text/names.html for a more in-depth discussion of how references work in Python.
Python not saving variable values when they are changed inside a function
Python can read from global variables inside a function, but can't assign them without some extra work. In general, when you want to use a global variable, it's a good idea to make it explicit by using the global
keyword in your function:
my_global_var = 0
def some_function():
global my_gobal_var
my_global_var = 10
print(my_global_var) # it prints 10
somefunction() # modifies the global var
print(my_global_var) # now it prints 10
Python: Keep changes on a variable made within a function
This is an example of passing variables to functions by value. By default, when you pass a variable to a function in Python, it is passed by value.
What it means is, that a new variable with a new scope is created with the same value of x
. So, any change that happens to the new x
is not reflected to the x
outside the function's scope.
If you want to get the value from the function back, you can use the return statement (as you have used). return
returns the value back from the function. However, in your example there is no variable to receive it. Hence, it is lost.
You would have to call the function as x = test(x)
. This ensures that x
receives the value back from the function.
Related Topics
What Is the G Object in This Flask Code
Calculate Mean Across Dimension in a 2D Array
Getting a Callback When a Tkinter Listbox Selection Is Changed
Combine Lists with Common Elements
Convert a List of Characters into a String
Django Db Settings 'Improperly Configured' Error
Pandas/Python: Set Value of One Column Based on Value in Another Column
Django Upgrading to 1.9 Error "Appregistrynotready: Apps Aren't Loaded Yet."
Pandas "Can Only Compare Identically-Labeled Dataframe Objects" Error
What Is the Most Pythonic Way to Pop a Random Element from a List
What Do the Python File Extensions, .Pyc .Pyd .Pyo Stand For
How to Properly Assert That an Exception Gets Raised in Pytest
In Python, How to Load Yaml Mappings as Ordereddicts