Python:将可变对象传递给方法

4

我正在尝试实现一个类,其中一个方法会调用该类中的另一个方法并传递一个对象作为参数。被调用的方法将会改变这个对象。我的实现比较复杂,所以我将贴出一些虚拟代码,让你们理解我的意思:

class test:
    def __init__(self,list):
        self.obj = list
    def mult(self, x, n):
        x = x*n
    def numtimes(self, n):
        self.mult(self.obj, n)

现在,如果我创建这种类型的对象并运行numtimes方法,它不会更新self.obj:

m = test([1,2,3,4])
m.numtimes(3)
m.obj  #returns [1,2,3,4]

我希望它给我返回 [1,2,3,4,1,2,3,4,1,2,3,4]。

基本上,我需要将self.obj传递给mult方法,并使其改变self.obj,这样当我调用m.obj时,我将得到[1,2,3,4,1,2,3,4,1,2,3,4]而不是[1,2,3,4]。

我感觉这只是理解Python如何将对象作为参数传递给方法的问题(就像它正在复制对象一样,而我需要使用指针),但也可能不是。我刚开始学习Python,真的需要一些帮助。

提前感谢!!


Python没有指针;当你将一个变量传递给一个函数时,你只是将函数参数与变量绑定到相同的值上。如果这是一个可变值,你可以改变它。你的问题是x = whatever只是重新绑定了局部变量x,它并没有改变任何东西。 - abarnert
同时,您真的需要就地修改self.obj吗?如果mult仅仅返回x*n,并且numtimes执行self.obj = self.mult(self.obj, n),那么会起作用吗?这种方法并不适用于每个使用案例(例如,如果self.obj是一个10000x10000的矩阵,您可能不希望生成额外的副本;如果其他代码引用了self.obj并且需要看到变化,复制也无济于事)...但在某些情况下,它通常更简单。 - abarnert
谢谢你澄清这个问题。回想起来,我觉得在'mult'方法中,我应该还包括一个数组元素的赋值操作,因为那才是我试图做的最重要的操作。我之前只是简单地使用了'x=x*n',但我意识到这有点让人困惑。根据'mult'方法中的一些逻辑,我真的需要改变'self.obj'的元素。 - nViz
2个回答

5

让我来谈一谈可变性的大主题。

列表是可变对象,支持可变操作和不可变操作。这意味着,既可以进行原位更改列表的操作,也可以返回一个新列表的操作。相比之下,元组只是不可变的。

因此,要对列表进行乘法运算,可以选择两种方法:

  1. a *= b

这是一种可变操作,会原地更改 'a'。

  1. a = a * b

这是一种不可变操作。它将评估 'a*b',创建一个正确值的新列表,并将 'a' 分配给该新列表。

在这里,已经有了解决问题的方案。但我建议您再往下阅读一些内容。当您将列表(和其他对象)作为参数传递时,您只传递了一个新引用或指向同一列表的“指针”。因此,在该列表上运行可变操作也会更改您传递的那个列表。这可能导致非常微妙的错误,例如您写下:

>>> my_list = [1,2,3]
>>> t = test(my_list)
>>> t.numtimes(2)
>>> my_list
[1,2,3,1,2,3]  # Not what you intended, probably!

所以这是我的最终建议。您可以选择使用可变操作,没有问题。但是要从您的参数中创建一个新副本,如下所示:

def __init__(self,l):
    self.obj = list(l)

或者使用不可变操作,并将其重新分配给self:

def mult(self, x, n):
    self.x = x*n

或者两者都做,多保险也没有坏处 :)

有没有可变数组赋值操作?所以,我可以将self.obj作为参数传递给mult函数吗?在这个函数中,我将部分内容复制到一个虚拟列表中,并将(排序后的)版本重新复制回self.obj,这样我以后就可以在该方法之外访问self.obj的排序版本了。 - nViz
1
你是否正在寻找对列表进行可变的“赋值”?因为这是可能的:my_list[:] = [1,2,3,4]此外,你可以使用切片来修改列表的一部分,例如改变最后三个元素:my_list[-3:] = [1,2,3] - Erez

3

乘法运算符 x * n 会创建一个新的实例,而不会改变现有的列表。请参见此处:

a = [1]
print (id (a) )
a = a * 2
print (id (a) )

这应该能正常工作:

class test:
    def __init__(self,list):
        self.obj = list

    def mult(_, x, n):
        x *= n

    def numtimes(self, n):
        self.mult(self.obj, n)

2
好的答案,但它并没有真正解释x = x*nx *= n之间的区别。第一个只是调用了x.__mult__(n),这应该是一个非变异函数,并将结果赋给x,这不会影响到其他引用原始值的人(比如调用者)。而第二个调用了x.__imult__(n),如果可能的话应该是可变的,这意味着它确实会影响到其他引用原始值的人。或者也许只需展示a *= 2; print(id(a)),而不是试图解释细节。 - abarnert

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接