0

What strange magic is this?

def rotate_list(lst, n):
    n = n % len(lst)
    lst = lst[-n:] + lst[:-n]
    
def rotate_list_2(lst):
    lst[0], lst[1], lst[2], lst[3] = lst[3], lst[0], lst[1], lst[2]
    
                 
s1 = [1, 2, 5, 4]
rotate_list(s1, 1)
print(s1)

s1 = [1, 2, 5, 4]
rotate_list_2(s1)
print(s1)

Output:

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

It appears that although lists are generally mutable within functions, if a list with the same name is created, then the original list is unaffected by changes to the new list. Could someone please explain what is happening here please, in terms of scope and references?

How would I rotate the original list without having to manually update each value as in rotate_list_2()? Or would this kind of thing generally be done by working with new lists returned from a function?

Robin Andrews
  • 3,514
  • 11
  • 43
  • 111
  • because [short-description-of-the-scoping-rules](https://stackoverflow.com/questions/291978/short-description-of-the-scoping-rules) - you create a local variable inside the function. See below for how to fix – Patrick Artner Aug 25 '20 at 08:30
  • Becaue `lst[-n:] + lst[:-n]` creates a new list. Your function simply assigns that to a local variable, and the object is discarded when the function terminates. IOW, `rotate_list` **doesn't mutate the list at all**, and `rotate_list_2` does. If you *did* want to mutate the list with that approach, you could do `lst[:] = lst[-n:] + lst[:-n]` – juanpa.arrivillaga Aug 25 '20 at 08:35
  • So, just read the following: https://nedbatchelder.com/text/names.html note, this really has nothing to do with lists in particular. The semantics here are always the same. *Assignment never mutates*. – juanpa.arrivillaga Aug 25 '20 at 08:38

3 Answers3

2

Assigning to list in function doesn't change the original reference.
The assignment just references the local parameter lst on the new value.
The original list referenced outside ('before') the function remains intact.

Insead assign to it's elements with this syntax:

def rotate_list(lst, n):
    n = n % len(lst)
    lst[:] = lst[-n:] + lst[:-n]

s1 = [1, 2, 5, 4]
rotate_list(s1, 1)

# And it works like magic! :)
# [4, 1, 2, 5]
print(s1)
Aviv Yaniv
  • 6,188
  • 3
  • 7
  • 22
  • So if I assign to a list passed in as a function argument, the original list will no longer be visible to the function and a local list variable will be created instead? – Robin Andrews Aug 25 '20 at 08:33
  • A reference to the new list values @RobinAndrews – Aviv Yaniv Aug 25 '20 at 08:34
  • 1
    @RobinAndrews it's a parameter, *which is always local*. Although, assignments in a function scope create a local variable. But in this case, since it was a parameter, it was already local. – juanpa.arrivillaga Aug 25 '20 at 08:38
0

If you reassign the argument of a function, the value does not change outside of the function scope.

def test0(a):
    a = 10
    print(a)

x = 4
test0(x)
print(x)

This would result in

10
4

The reason why assigning values of an array works is that you're not assigning a new value to the argument itself. You're instead accessing the memory that the array reads from, and you're changing it. Thus, those changes will happen even for outer scopes.

-1

After change in the function you can return the list and capture it in function call like s=function(s)

Ave799
  • 187
  • 7