Python通过引用传递[重复]

Posted

技术标签:

【中文标题】Python通过引用传递[重复]【英文标题】:Python's Passing by References [duplicate] 【发布时间】:2017-08-12 00:06:36 【问题描述】:

您好,我想了解 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

是不是因为在第三个示例中,0 int 对象被解释器视为“无”,并且没有被识别为需要与变量“a”引用的对象不同的身份(2)?而在第二个示例中,“b”添加了两个不同的 int 对象,而解释器正在为要添加的这两个对象分配内存,这给变量“a”提供了与变量“b”不同的标识?

【问题讨论】:

很好的问题,将尝试回答,但您需要了解完全 Python 专用的概念。 @Adirio 刚刚意识到我说的是“内存地址”而不是身份,我进行了调整。目前正在从 C++ 过渡。 这与 Python 存储小整数的方式有关,以节省内存。您可能对this article 感兴趣。 脉冲“通过引用传递”将我指向那个方向(从 C++ 过渡),但我理解你的问题。我试着在下面用简单的方式回答。 Python interning 上已有 329 个帖子!请找出规范的副本。 【参考方案1】:

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

浮点数没有这种情况可能是因为可能的值有“无穷大”(不是无穷大,而是因为浮点数很大),因此与整数相比,通过不同方式计算相同值的机会非常低。

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

【讨论】:

从技术上讲,你应该说 CPython 缓存从 -5 到 256 的整数,因为在 Python 语言的其他实现中可能不是这样。 另外,有点令人困惑的措辞:“此范围内的所有值都具有相同的 id”。我知道你的意思,但对于 python 新手来说,这听起来像是你在说从 -5 到 256 的所有数字共享一个 id,这是不正确的。 @Caleb True 现在更清楚了,感谢您的评论。【参考方案2】:

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

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

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

问题是CPython“缓存”了一些常见的整数值,这样就不会有多个具有相同值的对象以节省内存,而2是那些缓存的整数之一,所以每次变量指向整数 2 它将具有相同的 id(对于不同的 python 执行,它的值会有所不同)。

【讨论】:

【参考方案3】:

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

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

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

在处理 Python 时不要考虑“引用”和“价值”,适用于 C 的模型在这里并不适用。而是考虑“名称”和“对象”。

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

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

【讨论】:

我不喜欢使用“引用”这个词,因为它带来了不必要的 C 包袱(关于内存位置的详细信息,我们在 Python 中不需要关心,因为我们在更高层次的抽象)。它是一个name,因为它存在于某个namespace中。 我不想学究气,但它是文字字符串 "a""b" 是名称,而 ab (变量)是对对象的引用在记忆中。此外,在使用 Python 编程时,您应该注意引用,我已经看到了很多由于对象的引用计数下降到零(并且对象被垃圾回收)而发生的错误。 @a_guest 你是怎么遇到的?在 Python 中,引用计数位于幕后,因此除非您使用来自 weakref 模块的类或有缺陷的 C 扩展(或正在编写自己的扩展),否则您不应该遇到对象仍然可以访问的这种情况。 @JAB 公平地说,我只在使用PyQt 时才看到它。如果您不保留小部件的引用,则可能会发生各种有趣的事情。大多数时候它们只是消失了,但有时情况会更微妙一些,例如见this question。 我不明白你不同意什么。我已经在答案中写到“对象可以在没有任何名称的情况下存在”,例如在L = [1, [], 'hello'] 中,“内部”列表有一个引用(通过L[1])但范围内没有名称。无论您使用何种术语,都需要说明此类示例,因此最好选择不同的词来分别解释名称和引用的概念。

以上是关于Python通过引用传递[重复]的主要内容,如果未能解决你的问题,请参考以下文章

Python和Java参数传递[重复]

通过引用传递或通过复制传递 - Ruby 模块 [重复]

通过引用传递对象[重复]

通过引用或值传递的数组[重复]

通过引用javascript传递原始变量[重复]

在c / c ++中何时通过引用传递以及何时通过值传递[重复]