Python 的引用传递

Python's Passing by References

您好,我想了解 Python 的引用传递是如何工作的。 我有一个例子:

>>>a = 1
>>>b = 1
>>>id(a);id(b)
140522779858088
140522779858088

这很有意义,因为 a 和 b 都引用相同的值,它们将具有相同的值。我不太明白的是这个例子:

>>>a = 4.4
>>>b = 1.0+3.4
>>>id(a);id(b)
140522778796184
140522778796136

与本例不同的是:

>>>a = 2
>>>b = 2 + 0
>>>id(a);id(b)
140522779858064
140522779858064

是否因为在第 3 个示例中,0 int 对象被解释器视为 "None",并且未被识别为需要与变量 "a" 引用的对象不同的标识(2)?而在第二个示例中,"b" 正在添加两个不同的 int 对象,并且解释器正在为要添加的这两个对象分配内存,这为变量 "a" 提供了与变量 "b" 不同的标识?

here 所述,CPython 缓存从 -5 到 256 的整数。因此,此范围内具有相同值的所有变量共享相同的 ID(不过,我不会在未来的版本中打赌,但这是当前的实现)

浮点数没有这样的东西可能是因为有 "infinity" 个可能的值(不是无限的,但由于浮点数很大),所以通过不同的方式计算相同值的机会比较低到整数。

>>> a=4.0
>>> b=4.0
>>> a is b
False

在您的第一个示例中,名称 ab 都是 "referencing" 同一个对象,因为 interning。赋值语句产生一个具有相同 id 的整数,只是因为它重用了一个恰好已经存在于内存中的预先存在的对象。这不是整数的可靠行为:

>>> a = 257
>>> b = 257
>>> id(a), id(b)
(30610608, 30610728)

如上所示,如果您选择一个足够大的整数,那么它的行为将与第二个示例中的浮点数相同。在 Python 语言中,实习小整数是 optional 无论如何,这恰好是 CPython 实现细节:这是一种性能优化,旨在避免开销创建一个新对象。我们可以通过缓存常用的整数实例来加快速度,但代价是 Python 解释器占用更多内存。

在处理Python时不要考虑"reference"和"value",适用于C的模型在这里并不适用。而是想想 "names" 和 "objects"。

上图说明了您的第三个示例。 2 是对象,ab 是名称。我们可以有不同的名字指向同一个对象。对象可以没有任何名称而存在。

仅分配一个变量附加一个名称标签删除一个变量只会删除一个名称标签。如果您牢记这个想法,那么 Python 对象模型将再也不会让您感到惊讶。

Python 变量总是对对象的引用。这些对象可以分为可变对象和不可变对象。

可以在不修改其 id 的情况下修改可变类型,因此指向该对象的每个变量都将得到更新。但是,不可变对象不能这样修改,所以 Python 生成一个新的对象,并重新分配变量指向这个新对象,因此变量在更改之前的 id 将与 id 不匹配更改后的变量。

整数和浮点数是不可变的,因此在更改后它们将指向不同的对象,因此具有不同的 ID。

问题是CPython "caches"一些常见的整数值,所以为了节省内存,不会有多个具有相同值的对象,而2是其中一个被缓存的对象整数,所以每次一个变量指向整数 2 时,它都会有相同的 id(它的值对于不同的 python 执行是不同的)。