Python 为什么我的函数会覆盖作为参数传递的列表?
我创建了一个以列表为参数的函数。它洗牌列表,替换第一个元素并返回新列表Python 为什么我的函数会覆盖作为参数传递的列表?,python,arrays,list,Python,Arrays,List,我创建了一个以列表为参数的函数。它洗牌列表,替换第一个元素并返回新列表 import random firstList=["a","b","c","d","e","f","g","h","i"] def substitution(importedList): random.shuffle(importedList) importedList[0]="WORD" return importedList 洗牌对我的问题没有影响。然而,我惊讶地发现返回的importedLi
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
它不会取代第一个列表。第一个列表通过引用传递,这意味着在作为参数传递的列表上执行的任何突变也将在函数外部的列表上执行,因为它是相同的列表
但是,字符串和其他基本类型不是通过引用传递的,因此,您在函数范围中所做的任何更改都只是变量的本地副本 正如您所发现的,
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
它实际上与问题第一个代码块中的替换
列表中的代码并不相似。使用列表的等效代码如下:
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\u case,我有点纳粹)
[更新]
然而,您现在所拥有的是他已经尝试过的。“我通过在函数中复制列表找到了一个解决方法,但它似乎效率低下”
列表副本并不像你想象的那么低效,但这不是重点:正如其他人指出的那样,要么你对列表进行适当的修改,什么也不返回,要么返回一个新的列表——你不能吃不消。字符串、int、元组都是不可变的python类型,所以当你执行更改其中一种类型的操作时,n每次都会在内存中有效地创建对应的ew对象。(或者,如果试图就地更改这些对象,则会出现错误。) 列表和字典是可变的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)
深拷贝和浅拷贝的区别是
在执行浅层复制时,如果可变对象包含其他可变对象,则只复制顶部的对象。即,如果列表包含其他列表,如果复制顶部列表,然后在副本中更改内部列表,则实际上内部列表在副本和原始中都会更改,因为内存中的引用对象是相同的在两个不同的列表中创建一个新的对象,该对象的引用与原始对象中存储的引用相同
在执行深度复制时,如果可变对象包含其他可变对象,则内部可变对象也会被复制。例如,如前一个示例所示,如果在副本中更改内部列表,则仅在副本中更改,而原始对象不受影响。因此,深度复制复制会复制所有内容,在内存中为要复制的对象中的所有内容创建新的结构ied,而不仅仅是引用。从上的random.shuffle()
:shuffle list x就位;返回None。
如果不需要,可以使用:
1.
shuffle
更改原始可变列表。2.传递列表的copy
,而不是原始列表。请看字符串是不可变的,列表是可变的。按照@BhargavRao的建议传递副本。这可能是一件有趣的事情,但我宁愿让函数复制列表并处理副本,而不是我必须传递副本。可能是吗对于可以传入的类型,使函数更加灵活。(例如,可以传入元组而不是列表)。来自Bhargav的链接:“当你说new_list=my_list
你不是在复制,你只是在内存中添加指向原始列表的另一个名称。”。对于作为函数参数传递的列表也是如此。如果您想要的是创建副本,那么显式创建副本显然不是低效的——可以说,不这样做就无法做到:-)random.shuffle()
返回None。如果他这样定义某个列表
,他会发现一些意想不到的行为。事实上,他刚刚发现了它。然而,你现在所做的就是他已经尝试过的。我已经找到了一个解决方法,通过在函数中复制列表,但它似乎效率很低。它没有你想象的那么低,因为在Python中,每个列表都是无效的元素只是一个引用,它不会复制所有字符串的空间。它更像是C中的一个空指针数组。除非它处于一个紧密的循环中,否则即使对于大型列表,您也可以在大多数情况下承受它。我没有说它效率低下。OP是希望有所不同的对象。“通过引用传递”在讨论Python的数据模型时可能会误导术语
import copy
list_b = copy.deepcopy(list_a)
def substitutionandcopy(importedList):
shuffledList = random.sample(importedList, len(importedList))
shuffledList[0]="WORD"
return shuffledList