• python 引用和对象理解


    转载:ShaunChen

    今天浏览博客的时候看到这么一句话: python中变量名和对象是分离的

    最开始的时候是看到这句话的时候没有反应过来。决定具体搞清楚一下python中变量与对象之间的细节。(其实我感觉应该说 引用和对象分离更为贴切)

    从最开始的变量开始思考:

    在python中,如果要使用一个变量,不需要提前进行声明,只需要在用的时候,给这个变量赋值即可 (这个和C语言等静态类型语言不同,和python为动态类型有关)。

    第一个例子:

    a = 1

    这是一个简单的赋值语句,整数 1 为一个对象,a 是一个引用,利用赋值语句,引用a指向了对象1;这边形象比喻一下:这个过程就相当于“放风筝”,变量a就是你手里面的“线”,python就跟那根“线”一样,通过引用来接触和拴住天空中的风筝——对象。

    你可以通过python的内置函数 id() 来查看对象的身份(identity),这个所谓的身份其实就是对象的内存地址

    注:python一切皆对象的理念,所以函数也是一个对象,因此可以使用 id() 函数的__doc__方法来查看这个函数的具体描述

    >>> id.__doc__
        "id(object) -> integer
    
    Return the identity of an object. This is guaranteed to be unique among
    simultaneously existing objects.       (Hint: it's the object's memory address.)"

    第二个例子:

    a = 2

    a = 'banana'

    利用上面第一个栗子用到的 id()函数: 

    >>> a = 1
    >>> id(a) 
    24834392
    >>> a = 'banana'
    >>> id(a)
    139990659655312

    第一个语句中, 2是储存在内存中的一个整数对象,通过赋值 引用a 指向了 对象 1

    第二个语句中,内存中建立了一个字符串对象‘banana’,通过赋值将引用a 指向了 ‘banana’,对象1不再有引用指向它,它会被python的内存处理机制给当垃圾回收,释放内存。

    第三个例子:

    a = 3

    b = 3

    通过函数查看 变量a 和 变量b的引用情况: 

    >>> a = 3
    >>> b = 3
    >>> id(a)
    10289448
    >>> id(b)
    10289448

    在这里可以看到 这俩个引用指向了同一个 对象,这是为什么呢? 这个跟python的内存机制有关系,因为对于语言来说,频繁的进行对象的销毁和建立,特别浪费性能。所以在Python中,整数和短小的字符,Python都会缓存这些对象,以便重复使用。

    第四个例子:

    1.  a = 4

    2.  b = a(这里就是让引用b指向引用a指向的那个对象)

    3.  a = a + 2

    通过函数查看引用情况:

    当执行到第2步的时候,查看一下 a 和 b 的引用: 

    >>> a = 4
    >>> b = a
    >>> id(a)
    36151568
    >>> id(b)
    36151568

    可以看到 a 和 b 都指向了 整数对象 4

    接下来指向第3步:

    >>> a = a+2
    >>> id(a)
    36151520
    >>> id(b)
    36151568

    可以看到 a 的引用改变了,但是 b 的引用未发生改变;a,b指向不同的对象; 第3句对 a 进行了重新赋值,让它指向了新的 对象6;即使是多个引用指向同一个对象,

    如果一个引用值发生变化,那么实际上是让这个引用指向一个新的引用,并不影响其他的引用的指向。从效果上看,就是各个引用各自独立,互不影响。

    第五个例子(这个例子会涉及到 python中的 可变数据类型 和 不可变数据类型):

    开始这个例子之前,请记得注意到第四个例子的不同之处。

    1.   L1 = [1, 2, 3]

    2.   L2 = L1

    3.   L1[0] = 10

    通过函数查看引用情况:

    当执行第1步 和 第二步 的时候,查看一下 L1 和 L2 的引用情况:

    >>> L1 = [1,2,3]
    >>> L2 = L1
    >>> id(L1)
    139643051219496
    >>> id(L2)
    139643051219496

    此时 L1 和 L2 的引用相同,都是指向 [1,2,3]这个列表对象。

    接下来,继续执行第3步:

    >>> L1[0] = 10
    >>> id(L1)
    139643051219496
    >>> id(L2)
    139643051219496
    >>> L2
    [10, 2, 3]

    同样的跟第四个例子那样,修改了其中一个对象的值,但是可以发现 结果并不与第四个例子那样, 在本次实验中,L1 和 L2 的引用没有发生任何变化,但是列表对象[1,2,3] 的值 变成了 [10,2,3](列表对象改变了)

    在该情况下,我们不再对L1这一引用赋值,而是对L1所指向的表的元素赋值。结果是,L2也同时发生变化。

    原因何在呢?因为L1,L2的指向没有发生变化,依然指向那个表。表实际上是包含了多个引用的对象(每个引用是一个元素,比如L1[0],L1[1]..., 每个引用指向一个对象,比如(1,2,3) 。而L1[0] = 10这一赋值操作,并不是改变L1的指向,而是对L1[0], 也就是表对象的一部份(一个元素),进行操作,所以所有指向该对象的引用都受到影响。(与之形成对比的是,我们之前的赋值操作都没有对对象自身发生作用,只是改变引用指向。)

    看一个例子:

    L1 = [1, 2, 3]
    L2 = L1
    print(id(L1))
    print(id(L2))
    print(id(L1[0]), id(L1[1]), id(L1[2]))
    
    L1[0] = 10
    print(L1, L2)
    print(id(L1[0]), id(L1[1]), id(L1[2]))

    输出结果:

    2276118458184
    2276118458184
    1424059872 1424059904 1424059936
    [10, 2, 3] [10, 2, 3]
    1424060160 1424059904 1424059936

    发现改变的仅仅是id(L1[0]),与以上结论相符。

    列表可以通过引用其元素,改变对象自身(in-place change)。这种对象类型,称为可变数据对象(mutable object),词典也是这样的数据类型。

    而像之前的数字和字符串,不能改变对象本身,只能改变引用的指向,称为不可变数据对象(immutable object)。

    我们之前学的元组(tuple),尽管可以调用引用元素,但不可以赋值,因此不能改变对象自身,所以也算是immutable object.

    is关键字:

    当然,我们也可以要想知道是否指向同一个对象,我们可以使用 python的 is 关键词,is用于判断两个引用所指的对象是否相同。

    就像上述第四个例子 当进行到第1步和第2步的时候:

    >>> a = 4   ……id(a) = 36151568
    >>> b = a   ……id(b) = 36151568
    >>> a is b  
    True

    当进行到第3步的时候:

    >>> a = a + 2  ……id(a) = 36151520
    >>> a is b     ……id(b) = 36151568
    False

    突然想到,对于python 的 深拷贝 和 浅拷贝 的理解,也是可以根据这个进行验证,可以通过第五个例子进行辅助理解。

  • 相关阅读:
    yum downgrade降级安装包
    yapi内网部署
    tomcat启动提示"java.net.BindException: 地址已在使用"
    u盘安装centos系统识别nvme固态硬盘
    识别thinkpad USB2.0 or USB3.0
    window10激活
    onedriver business到期
    实用站点分享
    window挂载synology目录
    Spring注解@Qualifier的详细用法
  • 原文地址:https://www.cnblogs.com/fuqia/p/8954369.html
Copyright © 2020-2023  润新知