【问题标题】:What is an object reference in Python?Python中的对象引用是什么?
【发布时间】:2016-05-31 01:12:10
【问题描述】:

一本Python入门教科书定义了“对象引用”如下,但没看懂:

对象引用只不过是对象身份(存储对象的内存地址)的具体表示。

教科书试图通过使用箭头来说明这一点,将对象引用显示为从变量a 到赋值语句a = 1234 中的对象1234 的某种关系。

根据我从Wikipedia 收集的信息,a = 1234 的(对象)引用将是a 和1234 之间的关联,a 是“指向”1234(随意澄清“引用与指针”),但验证起来有点困难,因为(1)我正在自学 Python,(2)许多搜索结果都在谈论 Java 的引用,以及( 3) 没有多少搜索结果是关于 object 引用的。

那么,什么是 Python 中的对象引用?感谢您的帮助!

【问题讨论】:

  • Python 知道名称和值。一个很好的阅读是nedbatchelder.com/text/names.html
  • 它指的是 C 和相关语言中的一个众所周知的概念,并试图为您将其与该概念联系起来。如果你不熟悉 C 的概念,你可能想跳过那个解释,因为它试图连接到你还不熟悉的其他东西。
  • Java 使用与 Python 几乎相同的引用语义,因此这些搜索结果仍然是相关的。
  • @timgeb 啊,如果我对博客文章的理解正确,那么在a = 1234 的情况下,a 引用了对象1234,这种引用对象的行为称为一个“对象引用”。
  • @Logarhythms 赋值产生引用,a 是对对象的引用。

标签: python oop object reference


【解决方案1】:

对象是事物。通常,它们就是您在等式右侧看到的内容。

变量names(通常称为“名称”)是对实际对象的引用。当名称位于方程式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

我们可以通过查看a 和b 的id 来证明它们是同一个对象,并注意到它们是相同的。或者,我们可以看看改变a 或b 引用的对象的“副作用”(如果我们改变一个,我们同时改变两个,因为它们引用了同一个对象)。

a.append(1)
print a, b  # look mom, both are [1]!

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

【讨论】:

    【解决方案2】:

    任何与变量名相关的东西都必须存储在程序内存中的某个地方。想到这一点的一个简单方法是,内存的每个字节都有一个索引号。为简单起见,让我们想象一台简单的计算机,这些索引号从 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
    

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

    【讨论】:

      【解决方案3】:

      在 python 中,严格来说,该语言只有对对象的命名引用,它们表现为标签。赋值运算符仅绑定到名称。对象将一直保留在内存中,直到它们被垃圾回收

      【讨论】:

        猜你喜欢
        • 2019-10-12
        • 2016-08-22
        • 1970-01-01
        • 2012-03-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-08-15
        • 2016-10-07
        相关资源
        最近更新 更多