为什么我不能在函数内使用kwargs更改变量?

时间:2014-12-24 09:28:33

标签: python python-3.x dictionary kwargs

Python初学者的问题。我试图改变一个函数内部的一些变量的值,我不明白为什么有时候它会起作用,有时却不行。所以我想知道幕后发生了什么。如果我写:

def assign(self, **kwargs):

    kwargs['test'] = 3
    kwargs['steps'] += [1]

t = 1
s = []

assign(test=t, steps=s)

print(t)
print(s)

仍会打印

1
[]

现在,如果我将功能分配更改为

def assign(self, **kwargs):

    kwargs['test'] += 3
    kwargs['steps'] += [1, 2, 3]

它会更改列表但不会更改整数。所以我想这与整数是不可变的并且列表是可变的这一事实有关。所以我想改用字典,以确保我的变量发生了变化。那么:

dict = {'test':1, 'steps': []}
assign(**dict)
print(dict)

仍然打印

{'test': 1, 'steps': [1, 2, 3]}

具有完全相同的行为,所以现在我真的很困惑。似乎在解压缩字典时,我不再传递对字典变量的引用,以便这些unpacked变量按值复制?什么是实现我尝试做的最佳方式?

更新

感谢与@ 6502的讨论,自

以来
  

在Python中,无法更改已传递给函数的参数,因为您无法使用“指针”或“引用”。

     

最狡猾的方式不是这样做的。函数接收参数并提供结果。如果参数是可变的,它可以改变它的状态,但是认为不需要改变调用参数本身。

然后我决定返回一个包含结果的词典:

def assign(self, **kwargs):

    kwargs['test'] += 3
    kwargs['steps'] += [1, 2, 3]
    return kwargs

dict = {'test':1, 'steps': []}
dict = assign(**dict)
print(dict)

这当然有效,但我想知道对大数据的影响,因为在我看来(来自C ++世界),有很多复制。

2 个答案:

答案 0 :(得分:2)

第一个例子是错误的,你得到s=[1]

这是因为列表s是一个参数steps,您可以更改此列表的内容。 更简单:

def assign(step):
    step += [1] # change the contents of the list

s = []
assign(step=s)
print(s) # gives [1]

这与关键字参数或字典扩展无关。如果您使用**或直接给出关键词,则参数绝对相同。

不要试图通过引用传递变量»。这对python来说是不可能的。改为使用返回值。

def assign(test, steps):
    return 3, steps + [1]

t, s = assign(3, [])

答案 1 :(得分:1)

合理化语义的一种简单方法是考虑所有Python值确实是指向对象的指针,并且这些指针总是按值传递。

如果你更改指向调用者的对象可以看到变异,但是如果你分配变量,你只是改变它所指向的内容而调用者不会注意到。

在Python中,无法更改已传递给函数的参数,因为您无法使用“指针”或“引用”。改变变量的唯一方法是使用它的名称。

解决方法(在Python 3中)是传递一个可以访问本地的闭包,例如:

def foo(x, y, set_result):
    set_result(x + y)

def bar():
    res = None
    def set_res(x):
        nonlocal res
        res = x
    foo(10, 20, set_res)
    print(res)

然而,很少需要这种技巧,因为在具有参考参数传递的语言中,最常见的用途是返回多个值,例如, (C ++)

bool read_xy(double& x, double& y);

其中函数需要返回三个值,x,y和成功标志。

在Python中,这不是必需的,因为你可以写

return x, y

并将其用作

x, y = read_xy()