任何与变量名相关的东西都必须存储在程序内存中的某个地方。想到这一点的一个简单方法是,内存的每个字节都有一个索引号。为简单起见,让我们想象一台简单的计算机,这些索引号从 0(第一个字节)向上到有多少字节。
假设我们有一个 37 字节的序列,人类可能会将其解释为一些单词:
"The Owl and the Pussy-cat went to sea"
计算机将它们存储在一个连续的块中,从内存中的某个索引位置开始。这个索引位置通常被称为“地址”。显然这个地址绝对只是一个数字,这些字母所在的内存的字节数。
@12000 The Owl and the Pussy-cat went to sea
所以在地址 12000 是 T,在 12001 是 h,12002 是 e ... 直到最后一个 a 在 12037。
我在这里强调这一点,因为它是每种编程语言的基础。那 12000 是这个字符串的“地址”。它也是对其位置的“参考”。对于大多数意图和目的,address 是 pointer 是 reference。不同的语言对这些有不同的句法处理,但本质上它们是相同的——处理给定数量的数据块。
Python 和 Java 试图尽可能地隐藏这个 寻址,而像 C 这样的语言很乐意公开指针的确切含义。
从中得出的结论是,object reference 是数据存储在内存中的位置的编号。 (就像pointer一样。)
现在,大多数编程语言都区分简单类型:字符和数字,以及复杂类型:字符串、列表和其他复合类型。这就是对对象的引用产生影响的地方。
所以当对简单类型执行操作时,它们是独立的,它们都有自己的内存用于存储。想象一下python中的以下序列:
>>> a = 3
>>> b = a
>>> b
3
>>> b = 4
>>> b
4
>>> a
3 # <-- original has not changed
变量a 和b 不共享存储其值的内存。但是有一个复杂的类型:
>>> 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 - 它们共享相同的内存。等等,什么!在这里,我们发现s 和t 都是对同一个对象的引用——它们只是共享(指向)内存中的同一个地址。
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
因此请记住,通过引用使用的“对象”的内存由所有副本共享,而简单类型的内存则不是,很明显这两种类型的操作方式不同。