我创建了一个以列表作为参数的函数。它会对列表进行洗牌,替换第一个元素并返回新列表。
import random
firstList=["a","b","c","d","e","f","g","h","i"]
def substitution(importedList):
random.shuffle(importedList)
importedList[0]="WORD"
return importedList
洗牌对我的问题没有影响。但是,我很惊讶地看到返回的importedList覆盖了原始的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']
我通过复制函数中的列表找到了一种解决方法,但似乎效率低下。
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
我的问题是为什么函数会替换firstList?如果它是一个字符串,就不会发生这种情况。
string="a"
def substituteString(foo):
foo='b'
return foo
>>> string
'a'
>>> substituteString(string)
'b'
>>> string
'a'
答案 0 :(得分:4)
字符串,Ints,元组是不可变的python类型,因此当您执行更改其中一种类型的操作时,每次都会在内存中有效地创建新的相应对象。 (或者,如果尝试就地更改这些内容,则会出错。)
列表和字典是可变的python类型,因此当您执行更改其中一种类型的操作时,对象保持不变,但它的部分(即列表元素)会发生变化。
因此,当您想要更改列表但希望保留原始列表时,您必须自己复制它。重要的是,有两种类型的复制 - 浅拷贝和深拷贝。
浅拷贝可以这样完成:
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
深度复制按以下方式完成:
import copy
list_b = copy.deepcopy(list_a)
深拷贝和浅拷贝之间的区别是......
执行浅拷贝时,如果可变对象包含其他可变对象,则仅复制顶部对象。即如果列表包含其他列表,如果复制了顶部列表,然后在副本中更改了内部列表,则内部列表将在副本和原始列表中进行更改,因为它在内存中是相同的对象在两个不同的列表中引用。基本浅层复制创建一个新对象,其中相同的引用存储在原始对象中。
进行深度复制时,如果可变对象包含其他可变对象,则也会复制内部可变对象。即与前面的示例一样,如果更改副本中的内部列表,则仅在副本中更改,并且原始文件不受影响。因此,深拷贝会复制所有内容,在内存中为正在复制的对象中的所有内容创建新结构,而不仅仅是引用。
答案 1 :(得分:1)
它不会取代第一个列表。第一个列表通过引用传递,这意味着您在列表上执行的任何突变作为参数传递,也将在函数外部的列表上执行,因为它是相同的列表。
但是,字符串和其他基本类型不会通过引用传递,因此您在函数范围内所做的任何更改都只是变量的本地副本。
答案 2 :(得分:1)
正如您所发现的那样,random.shuffle
会改变列表:
random.shuffle(x [,random])
将序列x随机移动到位。可选参数random是一个0参数函数,在[0.0,1.0)中返回随机浮点数;默认情况下,这是函数random()。
注意,即使相当小的len(x),x的排列总数也大于大多数随机数生成器的周期;这意味着永远不会产生长序列的大多数排列。
字符串在Python中是不可变的,所有字符串操作都返回一个新字符串。这是"字符串"例子来自你的问题:
string="a"
def substitute_string(foo):
foo = 'b'
return foo
它实际上与问题的第一个代码块中substitution
列表中的代码无关。使用列表的等效代码是:
alist = [1, 2, 3]
def substitute_list(foo):
foo = [4, 5, 6]
return foo
它的作用相同:
>>> alist
[1, 2, 3]
>>> substitute_list(alist)
[4, 5, 6]
>>> alist
[1, 2, 3]
回到你的解决方案,它可能是:
def substitution_and_copy(imported_list):
imported_list = imported_list[:]
random.shuffle(imported_list)
imported_list[0]="WORD"
return imported_list
不,为参数赋值不会改变原始列表,就像在为foo
分配新值时不改变原始字符串一样(也改变了camelCase为snake_case,我对PEP8)很傻。
[更新]
然而,你现在拥有的是他已经尝试过的东西。 "我通过复制函数中的列表找到了一种解决方法,但它看起来效率低下了#34;
列表副本的效率并不像您想象的那么低,但这不是重点:正如其他人所指出的那样,您要么改变列表并且不返回任何内容或返回新列表 - 您不能这样做吃蛋糕然后吃。
答案 3 :(得分:0)
来自random.shuffle()
上的docs:shuffle list x in place; return None.
如果您不想要,可以使用random.sample():
def substitutionandcopy(importedList):
shuffledList = random.sample(importedList, len(importedList))
shuffledList[0]="WORD"
return shuffledList