Python中的对象引用是什么?

9
一本介绍Python的教材定义了“对象引用”如下,但我不理解:
“对象引用只是对象标识(存储对象的内存地址)的具体表示。”
该教材尝试通过使用箭头来显示对象引用作为某种关系,从变量a到赋值语句a = 1234中的对象1234。从我在{{link1:Wikipedia}}上获得的信息来看,“a = 1234”的(对象)引用将是a和1234之间的关联,其中a“指向”1234(请随时澄清“引用与指针”),但很难验证,因为(1)我正在自学Python,(2)许多搜索结果谈论Java的引用,(3)没有多少搜索结果与对象引用有关。
那么,在Python中什么是对象引用?感谢您的帮助!

6
Python知道名称和值。这篇文章介绍得很好:http://nedbatchelder.com/text/names.html - timgeb
1
这是指C语言和相关语言中一个众所周知的概念,并试图为您连接它。如果您不熟悉C语言的概念,您可能想跳过该解释,因为它试图连接到您尚未熟悉的其他内容。 - Two-Bit Alchemist
1
Java使用与Python几乎相同的引用语义,因此那些搜索结果仍然是相关的。 - Daniel Roseman
@timgeb 啊,如果我理解博客文章的内容正确的话,那么在a = 1234的情况下,a是对对象1234的一个引用,并且这个引用对象的行为被称为“对象引用”。 - vcyf56rtrfc
1
@Logarhythms 这个赋值语句是在引用对象,a 就是 对象的一个引用。 - timgeb
@timgeb 现在所有的一切都变得清晰明了。谢谢! - vcyf56rtrfc
4个回答

8

与变量名相关联的任何内容都必须在程序的内存中存储。一个简单的想法是,每个字节的内存都有一个索引号。为了简单起见,让我们想象一个简单的计算机,这些索引号从0(第一个字节)开始,向上增加到所有字节的数量。

假设我们有一个包含37个字节的序列,人类可能会将其解释为一些单词:

"The Owl and the Pussy-cat went to sea"

计算机将它们存储在内存中的某个索引位置开始的连续块中。这个索引位置通常被称为“地址”。显然,这个地址只是一个数字,即这些字母所在内存的字节数。
@12000 The Owl and the Pussy-cat went to sea

因此,在地址12000处是一个,在12001处是一个,12002处是一个 ... 最后一个位于12037处。
我强调这一点,因为它对每种编程语言都是根本的。12000是该字符串的“地址”。它也是它位置的“引用”。对于大多数意图和目的,地址是指针是引用。不同的语言对这些有所不同,但本质上它们是相同的 - 处理给定编号的数据块。
Python和Java尽可能地隐藏了这种寻址,而像C这样的语言很乐意暴露指针的真实面貌。
从中学到的,对象引用是存储在内存中的数据位置的数字。(指针也是如此。)
现在,大多数编程语言区分简单类型:字符和数字,以及复杂类型:字符串、列表和其他复合类型。这就是对象引用的区别所在。
因此,在执行简单类型操作时,它们是独立的,它们各自具有其自己的存储器。想象一下Python中的以下序列:
>>> a = 3
>>> b = a
>>> b
3
>>> b = 4
>>> b
4
>>> a
3      # <-- original has not changed

变量 ab 并不共享存储它们值的内存。但对于复杂类型:
>>> s = [ 1, 2, 3 ]
>>> t = s
>>> t
[1, 2, 3]
>>> t[1] = 8
>>> t
[1, 8, 3]
>>> s
[1, 8, 3]  # <-- original HAS changed

我们将变量t赋值为s,但是很明显在这种情况下,t就是s——它们共享相同的内存。等等,什么!我们发现st都是指向同一个对象的引用 - 它们只是简单地共享(指向)内存中的相同地址。
Python与其他编程语言的一个不同之处在于,它将字符串视为一种简单类型,这些字符串是独立的,因此它们的行为类似于数字:
>>> j = 'Pussycat'
>>> k = j
>>> k
'Pussycat'
>>> k = 'Owl'
>>> j
'Pussycat'  # <-- Original has not changed

C中,字符串被处理为复杂类型,并且会像Python列表示例一样运行。

所有通过引用处理的对象被修改时,所有对该对象的引用都能"看到"这个变化。因此,如果将对象传递给修改它的函数(即:更改包含数据的内存内容),那么这个变化也会反映在该函数之外。

但是,如果简单类型被更改或传递给函数,则会被复制到函数中,因此在原始函数中看不到变化。

例如:

def fnA( my_list ):
    my_list.append( 'A' )

a_list = [ 'B' ]
fnA( a_list )
print( str( a_list ) )
['B', 'A']        # <-- a_list was changed inside the function

但是:
def fnB( number ):
    number += 1

x = 3
fnB( x )
print( x )
3                # <-- x was NOT changed inside the function

记住,被引用使用的“对象”的内存是所有副本共享的,而简单类型的内存则不是,因此这两种类型的操作方式明显不同。


5

对象是"事物"。一般而言,它们是方程式右侧所看到的内容。

变量名称(通常称为“名称”)是对实际对象的引用。当名称出现在等式的右侧1时,它所引用的对象会被自动查找并用于方程式中。右侧表达式的结果是一个对象。等式左侧的名称则引用了这个(可能是新的)对象。

请注意,如果您正在使用容器对象(如列表或字典),也可以有不是显式名称的对象引用:

a = []  # the name a is a reference to a list.
a.append(12345)  # the container list holds a reference to an integer object

同样地,多个名称可以指向同一个对象:

a = []
b = a

我们可以通过查看 abid 并注意到它们相同来证明它们是相同的对象。或者,我们可以查看引用的对象 ab 的 "副作用"(如果我们改变一个,则两个都会改变,因为它们引用相同的对象)。
a.append(1)
print a, b  # look mom, both are [1]!

更准确地说,当一个名称在表达式中使用时。

0
在Python中,严格来说,语言只有对对象的命名引用,这些引用行为类似于标签。赋值运算符仅绑定到名称。对象将一直保留在内存中,直到它们被垃圾回收。

-1

首先,要记住 Python 中有两种类型的对象。

  1. 可变对象:其值可以更改。例如:字典、列表和用户定义的对象(除非定义为不可变)
  2. 不可变对象:其值无法更改。例如:元组、数字、布尔值和字符串。

现在,当 Python 说“按对象引用传递”时,只需记住:

如果底层对象是可变的,则所做的任何修改都将持久存在。

并且,

如果底层对象是不可变的,则所做的任何修改都不会持久存在。

如果您仍然需要示例以获得清晰度,请向下滚动或单击此处


问题是关于“对象引用”本身的含义,而不是调用约定或任何特定代码位的语义。 - Karl Knechtel
目前你的回答不够清晰,请编辑并添加更多细节,以帮助其他人理解它如何回答问题。你可以在帮助中心找到有关如何编写好答案的更多信息。 - user11717481
这并不是对问题的答案。一旦您拥有足够的声望,您将能够评论任何帖子;相反,提供不需要询问者澄清的答案。- 来自评论审核 - Ftagliacarne

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接