为什么我的函数会覆盖作为参数传递的列表?

时间:2016-02-16 11:50:30

标签: 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

洗牌对我的问题没有影响。但是,我很惊讶地看到返回的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'

4 个答案:

答案 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()上的docsshuffle list x in place; return None.如果您不想要,可以使用random.sample()

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