Why does my function overwrite the list passed as a parameter?

I created a function that takes a list as a parameter. It shuffles the list, replaces the first element, and returns a new list.

import random firstList=["a","b","c","d","e","f","g","h","i"] def substitution(importedList): random.shuffle(importedList) importedList[0]="WORD" return importedList 

Shuffling does not affect my question. However, I was surprised to see that the returned importList overwrites the original firstList.

 >>> firstList ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'] >>> substitution(firstList) ['WORD', 'a', 'b', 'd', 'i', 'c', 'g', 'e', 'h'] >>> firstList ['WORD', 'a', 'b', 'd', 'i', 'c', 'g', 'e', 'h'] 

I found a workaround by copying the list inside the function, but this seems inefficient.

 import random firstList=["a","b","c","d","e","f","g","h","i"] string="a" def substitutionandcopy(importedList): copiedList=importedList[:] random.shuffle(copiedList) copiedList[0]="WORD" return copiedList 

My question is: why does the function replace firstList? This would not happen if it were a string, for example.

 string="a" def substituteString(foo): foo='b' return foo 

 >>> string 'a' >>> substituteString(string) 'b' >>> string 'a' 
+5
source share
4 answers

Strings, Ints, Tuples are python immutable types, so when you perform operations that change one of these types, a new corresponding object is effectively created in memory every time. (Or you get an error if you try to change them in place.)

Lists and dictionaries are mutable types of python, so when you perform operations that change one of these types, the object remains the same, but its elements (i.e. list elements) change.

Therefore, when you want to change the list, but want to leave the original intact, you must copy it. It is important that there are two types of copying: shallow copy and deep copy .

A shallow copy can be made as follows:

 list_b = list_a[:] #using slice syntax #or list_b = list(list_a) #instantiating a new list from iterating over the old one #or import copy list_b = copy.copy(list_a) #using copy module 

A deep copy is performed as follows:

 import copy list_b = copy.deepcopy(list_a) 

The difference between a deep copy and a shallow copy ...

When making a shallow copy, if the mutable object contains other mutable objects, only the top one is copied. That is, if the list contains another list, if the top list is copied, and then the internal list is changed in the copy, in fact the internal list will be changed both in the copy and in the original, because it is the same object in memory that is referenced in two different list. Basically a shallow copy creates a new object with the same links that are stored in the original object.

When performing a deep copy, if the mutable object contains other mutable objects, then the internal mutable objects are also copied. That is, as in the previous example, if you change the internal list in the copy, it changes only in the copy, and the original does not change. So a deep copy copies everything, creates a new structure in memory for everything in the copied object, and not just for links.

+1
source

It does not replace the first list. The first list is passed by reference, meaning that any mutations that you execute in the list that is passed as a parameter will also be executed in the list outside the function, because it is the same list.

However, strings and other basic types are not passed by reference, so any changes you make in your scope make up only a local copy of this variable.

+1
source

As you know, random.shuffle mutates the list in place:

random.shuffle (x [, random])

Shuffle the sequence x into place. The optional random argument is a 0-argument function that returns a random float in [0.0, 1.0]; the default is the random () function.

Note that for even a relatively small len (x), the total number of permutations x is greater than the period of most random number generators; this means that most permutations of a long sequence can never be generated.

Strings are immutable in Python, all string operations return a new string. This is an example line from your question:

 string="a" def substitute_string(foo): foo = 'b' return foo 

This is not like the code from the substitution list in the first block of question code. The equivalent code using the list would be the following:

 alist = [1, 2, 3] def substitute_list(foo): foo = [4, 5, 6] return foo 

And it works the same way:

 >>> alist [1, 2, 3] >>> substitute_list(alist) [4, 5, 6] >>> alist [1, 2, 3] 

Back to your decision, it could be:

 def substitution_and_copy(imported_list): imported_list = imported_list[:] random.shuffle(imported_list) imported_list[0]="WORD" return imported_list 

And no, assigning a new value to an argument will not mutate the original list, just as you will not mutate the original string when you assign a new value to foo (also changed camelCase to snake_case, I'm a little annoying about PEP8 ).

[update]

Now you have what he has already tried. "I found a workaround by copying the list inside the function, but it seems inefficient"

A copy of the list is not as ineffective as you think, but it is not a problem: as someone else has noticed, either you mutate the list in place, or you don’t return anything or you don’t return a new list - you cannot eat the cake.

+1
source

From docs to random.shuffle() : shuffle list x in place; return None. shuffle list x in place; return None. If you do not want this, you can use random.sample () :

 def substitutionandcopy(importedList): shuffledList = random.sample(importedList, len(importedList)) shuffledList[0]="WORD" return shuffledList 
0
source

Source: https://habr.com/ru/post/1243129/


All Articles