Modifying a List Inside a Function

Modifying a list inside a function

If you assign something to the variable list_arg, it will from then on point to the new value. The value it pointed to before that assignment (your original list) will stay unchanged.

If you, instead, assign something to elements of that list, this will change the original list:

list_arg[:] = list(a)

This will make your code work as you wanted it.

But keep in mind that in-place changes are hard to understand and probably can confuse the next developer who has to maintain your code.

Modifying list inside function

This is because Python passes all arguments by passing object references, not names or references to names. There is NO relationship between the name list_a in your function and the global name list_a. Think about it this way:

def change_list( a, b ):
a = b

c = 3
d = 7
change_list(c,d)

After this code runs, c will still be 3. The function gets two parameters: the integer 3, and the integer 7. Those objects have NO CONNECTION to the names outside the function. Within the function change_list, a starts out bound to the integer 3. After the first line, a becomes bound to the integer 7. When the function ends, those names go away. Nothing has changed outside the function.

Your code is exactly the same, Your change_list receives two objects: a list containing ['a','a','a] and a list containing ['x','x','x']. The function knows absolutely nothing about what names those lists might be bound to outside the function. Inside the function, you change the local name list_a to be bound to the list ['x','x','x']. Then the function ends and those names evaporate. Nothing has changed outside the function.

Now, consider making the following change:

def change_list( list_a, list_b ):
list_a.extend(list_b)

Here, we are actually changing the list OBJECT that list_a is bound to. If you run that code, you'll see that the global list_a has also been changed. This is not because the names happen to be the same, but because we have changed the object that both names were bound to.

Python modifying list within function

You have to return and assign the returned value to list1:

def func(list1):
list1 = list1[2:]
print(list1) # prints [2, 3]
return list1

list1=func(list1)

or you have to reference list1 as global inside func().

Why the modifications of a list inside a function do not change the list?

nums = nums + [nums[-1] + 1]  # add a dummy element to the end of the list

Wrong. You specifically create a new list from nums and your new element, and then make your local variable nums point to that new list. You then operate nicely on the new list and exit the function without saving the result.

Try

nums.append(nums[-1] + 1)  # add a dummy element to the end of the list

Output:

[1, 2, 3, 4, 5, 3, 4, 4, 4, 5, 5]

Changing a list inside a function

The problem is that a copy of the list is modified in the function's environment, and that copy is destroyed when the function exits. R differs in this way from many other languages, in that the global environment is not (by default) modified within functions.

You should have your function return the new list:

get_letter <- function(a, letter) {
if (is.null(a[[letter]])) {
a[[letter]] <- replicate_letter(letter)
}
# Do further analysis, plotting,....

return(a)
}

Calling:

a <- get_letter(a, 'c')

Pythonic way to write a function which modifies a list?

TLDR: If you modify an argument, do not return it.


list.sort() sorts the list in place. In order to remind you of that fact, it does not return the sorted list.

Design and History FAQ

It is idiomatic to indicate whether you modify an argument by not returning it. For example, your first case is correct, your second is not:

def remove_one(b):
b.remove(1) # modify, do not return

If you do not modify an argument but create a copy, you must return the copy for the operation to be meaningful:

def one_removed(b):
c = b.copy() # create new object...
c.remove(1)
return c # ...and return it

It is common for function/method names to reflect whether they actively modify their argument. Modifying functions tend to use active verbs, whereas non-modifying functions tend to use passive verbs or adjectives.


There is no "pythonic" choice whether to modify the argument or not - operating on the original or a copy are two fundamentally different operations. Both are valid depending on the use-case.

>>> items = [4, 3, 10, 2, 5]
>>> sorted(items)
[2, 3, 4, 5, 10]
>>> items.sort()
>>> items
[2, 3, 4, 5, 10]

In general, mutating an argument is faster in Python, whereas creating a new result is easier to reason about.
In specific, comprehensions represent functional programming operations (namely map and filter) -- they are designed not to modify their iterable.


Whether to return an argument due to modification only concerns said argument. For example, list.pop modifies the list and does not return it -- however, it does return the popped element.

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.



Related Topics



Leave a reply



Submit