Python: 使用可变参数列表进行对象赋值

3 投票
3 回答
3238 浏览
提问于 2025-04-16 00:17

有没有一种方法可以让我们给一个函数传递可变数量的参数,并且用 ( *args, **keywords ) 这种方式来改变这些参数呢?我试过几种方法,但要么没有任何变化,要么编译器报错:

def foo( *args ):
    args[0] = 4

这让我遇到了 TypeError: object does not support assignment(因为它们是元组)。或者

def foo( *args ):
    plusOne = [ item+1 for item in args ]
    args = plusOne

这根本没有任何效果。如果没有办法或者解决方案,我可以认输。

编辑:

为了更清楚地说明我为什么想这样做,考虑下面的情况:

class bar(object):
    def __init__(self,obj):
        self.obj = obj

def foo( input ):
    input.obj = "something else"

如果我把我的 bar 对象传给 foo,我会看到状态发生变化。为了创建一个可以执行 deepcopy 的装饰器,重置所有这样的状态,我现在是为 N 个参数定制的。我想创建一个可以接受任意数量参数的装饰器。因此,我才有了这个问题。

3 个回答

1

如果你想要改变传给函数的参数,让它能够像这样工作:

>>> x, y = (10, 17)
>>> foo(x, y)
>>> print (x, y)
(11, 18)

那你就没那么幸运了,原因在于Mark的回答中已经说明了。

不过,如果你传递的是可变对象给你的函数,你可以改变这些对象,并在调用foo之后看到结果:

def foo(*args):
    for arg in args:
        arg['value'] += 1

>>> d1 = {'value': 1}
>>> d2 = {'value': 2}
>>> foo(d1, d2)
>>> print (d1, d2)
({'value': 2}, {'value': 3})
3

之所以

args[0] = 4

不管用,是因为错误信息提到,args 是一个元组,而元组是不可变的。这意味着你不能直接修改它。所以,你需要先把它转换成可以修改的对象,比如这样:

>>> def foo( *args ):
    print(args)
    args = list(args)
    args[0] = 42
    print(args)


>>> foo(23)
(23,)
[42]

如果你提供更多信息,就可以给出更符合Python风格的解决方案,因为你现在的做法看起来有点奇怪。而且,第二段代码似乎运行得很好。

比如下面这个代码运行得很好,并且能改变调用范围内的变量:

>>> def spam(*a):
    a[0][0] = 42


>>> l = [23, 32]
>>> spam(l)
>>> l
[42, 32]

原因是一样的:l 这个对象是可变的。你在你的例子中也可以看到这一点:

>>> def foo( *input ):
    input[0].obj = "something else"


>>> b = bar('abc')
>>> foo(b)
>>> b.obj
'something else'
4

不,Python使用的是一种叫做对象共享调用的方式,也可以称作值传递。

为了让术语更清楚:你得到的不是对象的深拷贝,而是对象引用的拷贝。注意:这和引用传递是不一样的!你可以把它理解为值传递,而这些值其实是指向对象的引用。

所以,回答你的问题,你得到的是参数的拷贝(对象引用)。你不能像引用传递那样修改这些对象引用。如果你想的话,可以创建一个新的修改过的拷贝,但从你的例子来看,这并不是你想要的。调用的作用域是看不到你的更改的。

如果你对收到的对象进行修改,那么客户端可以看到这些更改。

撰写回答