Python中的函数更改列表值而不是变量值[重复]
Posted
技术标签:
【中文标题】Python中的函数更改列表值而不是变量值[重复]【英文标题】:Function changes list values and not variable values in Python [duplicate] 【发布时间】:2013-07-15 05:27:27 【问题描述】:我们来看一个简单的代码:
y = [1,2,3]
def plusOne(y):
for x in range(len(y)):
y[x] += 1
return y
print plusOne(y), y
a = 2
def plusOne2(a):
a += 1
return a
print plusOne2(a), a
“y”的值发生变化,但“a”的值保持不变。我已经知道这是因为一个是可变的,另一个不是。但是如何修改代码让函数不改变列表呢?
例如做这样的事情(为了简单起见,用伪代码):
a = [1,2,3,...,n]
function doSomething(x):
do stuff with x
return x
b = doSomething(a)
if someOperation(a) > someOperation(b):
do stuff
编辑:抱歉,我有关于嵌套列表的另一个问题。请参阅此代码:
def change(y):
yN = y[:]
for i in range(len(yN)):
if yN[i][0] == 1:
yN[i][0] = 0
else:
yN[i][0] = 1
return yN
data1 = [[1],[1],[0],[0]]
data2 = change(data1)
在这里它不起作用。为什么?再次:如何避免这个问题?我理解为什么它不起作用:yN = y[:] 将 y 的值复制到 yN,但这些值也是列表,因此对于列表中的每个列表,操作必须加倍。如何使用嵌套列表进行此操作?
【问题讨论】:
一般来说,避免突变引起混淆的最简单方法是避免突变。当这不合适时,答案是制作一个副本并对其进行变异。真的没有其他选择了。 既然您接受了@kindall 的回答,也许您会发现this blog post 很有帮助。它更详细地解释了事情,(在我看来)更清晰。 【参考方案1】:您可以通过创建一个函数并通过映射函数调用此函数来做到这一点, map 函数将调用 add 函数并为其赋值,然后它将像这样打印新值:
def add(x):
return x+x
print(list(map(add,[1,2,3])))
或者你可以使用 (*range) 函数,就像那个例子一样很容易做到:
print ([i+i for i in [*range (1,10)]])
【讨论】:
【参考方案2】:Python 变量包含指向对象的指针或引用。所有值(甚至整数)都是对象,赋值会将变量更改为指向不同的对象。它不会在变量in 中存储新值,它会更改变量以引用或指向不同的对象。出于这个原因,许多人说 Python 没有“变量”,它有“名称”,而=
操作不是“为变量赋值”,而是“将名称绑定到对象。 "
在plusOne
中,您正在修改(或“变异”)y
的内容,但永远不要更改y
本身所指的内容。它始终指向同一个列表,即您传递给函数的列表。全局变量 y
和局部变量 y
引用同一个列表,因此使用任一变量都可以看到更改。由于您更改了传入的对象的内容,实际上没有理由返回y
(实际上,返回None
是Python 本身对像这样“就地”修改列表的操作所做的——值由创建新对象而不是改变现有对象的操作返回)。
在plusOne2
中,您正在更改局部变量a
以引用不同的整数对象3
。 (“将名称a
绑定到对象3
。”)全局变量a
并没有因此而改变,继续指向2
。
如果您不想更改传入的列表,请将其复制并更改。然后你的函数应该返回新列表,因为它是创建新对象的操作之一,如果你不返回它,新对象将会丢失。您可以将此作为函数的第一行:例如x = x[:]
(正如其他人指出的那样)。或者,如果以任一方式调用该函数可能有用,您可以让调用者传入x[:]
,如果他想要复制的话。
【讨论】:
这对新手来说有点混乱(因为没有人通过 Python 学习编程不知道指针是什么),并且会误导其他人(因为变量 不是 指针,它们是名称,问题在于绑定与变异,而不是指针变量与普通变量)。 我做了一些更改以澄清其中的一些内容。我希望。【参考方案3】:回答您编辑的问题:
复制嵌套数据结构称为深度复制。要在 Python 中执行此操作,请在 copy
模块中使用 deepcopy()
。
【讨论】:
【参考方案4】:创建列表的副本。使用testList = inputList[:]
。看代码
>>> def plusOne(y):
newY = y[:]
for x in range(len(newY)):
newY[x] += 1
return newY
>>> y = [1, 2, 3]
>>> print plusOne(y), y
[2, 3, 4] [1, 2, 3]
或者,您可以在函数中创建一个新列表
>>> def plusOne(y):
newList = []
for elem in y:
newList.append(elem+1)
return newList
您也可以使用其他人指出的推导式。
>>> def plusOne(y):
return [elem+1 for elem in y]
【讨论】:
哇,感谢您的快速响应!我试过 newY = y 但它没有用,而应该是简单的 newY = y[:]。newY = y
只是创建另一个对它的引用,而newY = y[:]
创建一个新列表。
是的,现在我明白了,一切都变得清晰了!谢谢!【参考方案5】:
正如其他人指出的那样,如果您不想修改原始列表,则应使用newlist = original[:]
或newlist = list(original)
复制列表。
def plusOne(y):
y2 = list(y) # copy the list over, y2 = y[:] also works
for i, _ in enumerate(y2):
y2[i] += 1
return y2
但是,您可以通过列表理解获得所需的输出
def plusOne(y):
return [i+1 for i in y]
这将遍历y
中的值并通过向每个值添加一个来创建一个新列表
【讨论】:
【参考方案6】:您可以使用切片表示法传递列表的副本:
print plusOne(y[:]), y
或者更好的方法是在函数本身中创建 list 的副本,这样调用者就不必担心可能的修改:
def plusOne(y):
y_copy = y[:]
改为使用y_copy
。
或者如 cmets 中@abarnet 所指出的,您可以修改函数以使用list comprehension
,这将完全创建一个新列表:
return [x + 1 for x in y]
【讨论】:
或者,更好的是,重写代码以便动态构建副本:y = [x+1 for x in y]
。
@abarnert。对:) 谢谢,编辑了答案。 :)【参考方案7】:
只需创建一个包含所需值的新列表,然后将其返回即可。
def plus_one(sequence):
return [el + 1 for el in sequence]
【讨论】:
以上是关于Python中的函数更改列表值而不是变量值[重复]的主要内容,如果未能解决你的问题,请参考以下文章