78

我对以下行为感到非常困惑。案例 1、3 和 4 的表现与我预期的一样,但案例 2 没有。为什么情况 2 允许函数全局更改字典条目的值,即使函数从未返回字典?我使用函数的一个主要原因是将函数中的所有内容与其余代码隔离开来,但如果我选择在函数内部使用相同的变量名,这似乎是不可能的。我的理解是,在函数中明确定义的任何内容都是该函数的本地内容,但如果字典被定义并作为输入传递给函数,情况似乎并非如此。

Python 2.7.2+ (default, Oct  4 2011, 20:06:09) 
[GCC 4.6.1] on linux2
Type "help", "copyright", "credits" or "license" for more information.

=============案例1================

>>> def testfun1(a):
...     a=2
... 
>>> a=0
>>> testfun1(a)
>>> a
0

=============案例2================

>>> def testfun2(b):
...     b['test']=2
... 
>>> b={}
>>> testfun2(b)
>>> b
{'test': 2}

=============案例3===============

>>> def testfun3():
...     c=2
... 
>>> c=0
>>> testfun3()
>>> c
0

=============案例 4=============== (由这个问题解释:全局字典不需要关键字 global 来修改它们?

>>> def testfun4():
...     d['test']=10
... 
>>> d={}
>>> testfun4()
>>> d
{'test': 10}
4

5 回答 5

152

Python 的“参数评估策略”与您可能习惯使用的语言有些不同。python没有显式的按值调用和按引用调用语义,而是通过共享调用。您本质上总是在传递对象本身,而对象的可变性决定了它是否可以被修改。列表和字典是可变对象。数字、字符串和元组不是。

您将字典传递给函数,而不是副本。因此,当您修改它时,您也在修改原始副本。

为避免这种情况,您应该在调用函数之前或从函数内部首先复制字典(将字典传递给dict函数应该这样做,即将函数testfun4(dict(d))定义为def testfun4(d):)。

于 2013-02-25T23:34:47.710 回答
11

为了支持@Casey Kuball所说的,Python 中的每个对象都是通过引用传递的。每个函数都会收到对您传递的实际对象的引用。修改这些对象取决于它们是否是可变数据类型。

本质上,可以说字典、集合和列表等可变对象是通过引用传递的。像int, str,等不可变对象tuple是按值传递的。

您还应该注意,在某些情况下,可变对象在函数中被覆盖,从而失去对传递给函数的实际对象的引用。

>>> def testfun(b):
...     b = b or {}  # Creates a new object if b is false
...     b['test'] = 2
... 
>>> b = {}
>>> testfun(b)
>>> b
{}
于 2019-06-05T10:17:50.403 回答
4

当您将整数或字符串等基本对象传递给函数时,如果您在函数内部对其进行更改,则函数外部的相应对象不会发生任何事情,因为当您使用基本对象时,python 按值传递它。

但是,如果您将字典或列表传递给函数,它们将通过引用传递,这意味着您将具有这种行为:函数外部的对象已更改,如您所见。

编辑: 此外,通过值传递或通过引用传递之间存在区别:通过值,对象的“副本”被制作以便在函数中使用;通过引用,完全相同的对象通过引用传递,并且在函数内部对其进行的修改在外部可见。根据定义,python 通过值传递其不可变对象,并通过引用传递其可变对象。

于 2013-02-25T23:38:22.390 回答
2

global 关键字仅用于赋值(很可能del,我从未尝试过)。对象突变是完全有效的。

于 2013-02-25T23:29:37.693 回答
1

你给函数传递了一个dict对象,在函数内部修改了,当然函数返回后也会修改。该对象没有被复制,因此您修改了您传递的同一对象,并且这个问题与命名、相似名称、范围等无关,因为您明确传递了该对象。

于 2013-02-25T23:32:32.400 回答