与变量名相关的任何内容都必须存储在程序内存中的某个位置。想到这一点的一个简单方法是,内存的每个字节都有一个索引号。为了简单起见,让我们想象一台简单的计算机,这些索引号从 0(第一个字节)向上到有多少个字节。
假设我们有一个 37 个字节的序列,人类可能会将其解释为一些单词:
"The Owl and the Pussy-cat went to sea"
计算机将它们存储在一个连续的块中,从内存中的某个索引位置开始。该索引位置通常称为“地址”。显然这个地址绝对只是一个数字,这些字母所在的内存的字节数。
@12000 The Owl and the Pussy-cat went to sea
所以在地址 12000 处是T
,在 12001h
, 12002 一个e
...直到最后a
于 12037。
我在这里强调这一点是因为它是每种编程语言的基础。 12000 就是这个字符串的“地址”。它也是对其位置的“参考”。对于大多数意图和目的address
is a pointer
is a 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
and 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
to be s
,但显然在这种情况下t
is s
- 他们共享相同的记忆。等等,什么!在这里我们发现两者s
and t
are a 参考指向同一个对象 - 它们只是共享(指向)内存中的相同地址。
Python 与其他语言的不同之处在于,它将字符串视为一种简单类型,并且它们是独立的,因此它们的行为类似于数字:
>>> j = 'Pussycat'
>>> k = j
>>> k
'Pussycat'
>>> k = 'Owl'
>>> j
'Pussycat' # <-- Original has not changed
而在C
字符串肯定会作为复杂类型进行处理,并且其行为类似于 Python 列表示例。
所有这一切的结果是,当修改通过引用处理的对象时,对此对象的所有引用都会“看到”更改。因此,如果将对象传递给修改它的函数(即:保存数据的内存内容发生更改),则更改也会反映在该函数之外。
但是,如果更改简单类型或传递给函数,则它是copied函数,因此在原始版本中看不到更改。
例如:
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
But:
def fnB( number ):
number += 1
x = 3
fnB( x )
print( x )
3 # <-- x was NOT changed inside the function
因此请记住,通过引用使用的“对象”的内存由所有副本共享,而简单类型的内存则不然,很明显这两种类型的操作不同。