zoukankan      html  css  js  c++  java
  • Python 从入门到进阶之路(七)

    之前的文章我们简单介绍了一下 Python 中异常处理,本篇文章我们来看一下 Python 中 is 和 == 的区别及深拷贝和浅拷贝。

    我们先来看一下在 Python 中的双等号 == 。

    == 是比较两个对象的内容是否相等,即两个对象的“值”是否相等,不管两者在内存中的引用地址是否一样。

    is 比较的是两个实例对象是不是完全相同,它们是不是同一个对象,占用的内存地址是否相同。即is比较两个条件:1.内容相同。2.内存中地址相同

     1 a = 10000
     2 b = 10000
     3 print(a == b)  # True
     4 print(a is b)  # True
     5 print(id(a))  # 4461408208
     6 print(id(b))  # 4461408208
     7 
     8 a = "hello world"
     9 b = "hello world"
    10 print(a == b)  # True
    11 print(a is b)  # True
    12 print(id(a))  # 4461408208
    13 print(id(b))  # 4461408208
    14 
    15 a = [11,22,33]
    16 b = [11,22,33]
    17 print(a == b)  # True
    18 print(a is b)  # False
    19 print(id(a))  # 4409720712
    20 print(id(b))  # 4409720776

    在上面的代码中,我们分别定义了 a 和 b 两个变量,通过输出结果可以发现当变量为字符串或数字时, is 和 == 的输出结果是一样的,当为列表时 is 和 == 结果不一样,通过打印两个变量的 id 值可以看出两个 id 值不一样,这是由于当我们创建列表 a 和 b 时,是分别开辟了两块内存来分别存储这两个变量,从表象上来看结果是一样的,但两个变量所指向的内存地址不一样,我们将上面的代码改为如下:

    1 a = [11,22,33]
    2 b = a
    3 print(a == b)  # True
    4 print(a is b)  # True
    5 print(id(a))  # 4535062408
    6 print(id(b))  # 4535062408

    在上面的代码中,我们并没有直接给变量 b 赋值,而是让 b=a,这样的话 b 和 a 就指向了同一块内存,所以 a is b 就为 True 了。

    上面的代码我是在 PyCharm 编辑器中实现的,但是在终端命令行实现的话结果却是不一样的,如下:

     1 >>> a = 10000
     2 >>> b = 10000
     3 >>> a == b
     4 True
     5 >>> a is b
     6 False
     7 >>> id(a)
     8 4360555120
     9 >>> id(b)
    10 4360555216

    当我们将 a 和 b 的值变小时,如下:

     1 >>> a = 100
     2 >>> b = 100
     3 >>> a == b
     4 True
     5 >>> a is b
     6 True
     7 >>> id(a)
     8 4357367984
     9 >>> id(b)
    10 4357367984

    造成上面的原因是因为python对小整数在内存中直接创建了一份,不会回收,所有创建的小整数变量直接从对象池中引用他即可。但是注意Python仅仅对比较小的整数对象进行缓存(范围为范围[-5, 256])缓存起来,而并非是所有整数对象。也就说只有在这个[-5,256]范围内创建的变量值使用is比较时候才会成立。

    在 PyCharm 中,当值超过 256 时 is 和 == 的输出结果仍是一样,这是因为解释器也做了一部分优化,对于数字和字符串这类变量都进行了缓存。

    我们再来看一下在终端命令行中当变量为字符串时:

     1 >>> a = "hello world"
     2 >>> b = "hello world"
     3 >>> a == b
     4 True
     5 >>> a is b
     6 False
     7 >>> id(a)
     8 4359747248
     9 >>> id(b)
    10 4361247408
     1 >>> a = "hello"
     2 >>> b = "hello"
     3 >>> a == b
     4 True
     5 >>> a is b
     6 True
     7 >>> id(a)
     8 4361199040
     9 >>> id(b)
    10 4361199040

    通过输出结果可以看出,在命令行中当变量为简单字符串时输出结果一致,否则输出结果不一致,这是由于 Python 对简单字符串对象也进行了缓存,这样做的意义是可以优化代码的运行速度,减少内存消耗。

    在上面的代码中,我们让 a=b 其实是一种浅拷贝的过程,他们指向的是同一块内存,当我们改变其中一个变量时,另一个也会变,如下:

    1 a = [11,22,33]
    2 b = a
    3 a[0] = 123
    4 print(a)  # [123, 22, 33]
    5 print(b)  # [123, 22, 33]

    在 Python 中还有深拷贝,它是重新开辟一块区域用来存储变量的,如下:

     1 import copy
     2 
     3 a = [11, 22, 33]
     4 b = a
     5 c = [11, 22, 33]
     6 d = copy.deepcopy(a)
     7 print(a == b, a is b)  # True True
     8 print(a == c, a is c)  # True False
     9 print(a == d, a is d)  # True False
    10 
    11 a[0] = 123
    12 print(a)  # [123, 22, 33]
    13 print(b)  # [123, 22, 33]
    14 print(c)  # [11, 22, 33]
    15 print(d)  # [11, 22, 33]

    在上面的代码中我们引入了一个 copy 的模块,引入模块这个之前的文章没有提到,后续会单独说一下。

    在 copy 模块中有一个 deepcopy() 的方法,从上面的代码中可以看出 deepcopy() 的方法就相当于 c=[11,22,33] 这样重新开辟一块区域来存储变量,当 a 变量改变时,浅拷贝的内容 b 会随之改变,但是深拷贝的内容 d 并不是指向 a 的内存地址,所以不会改变。

    在 copy 的模块中还有一个 copy() 方法,它在某些方面看起来和 deepcopy() 方法是一样的,其实不一样,如下:

    import copy
    
    a = [11, 22, 33]
    b = copy.copy(a)
    c = copy.deepcopy(a)
    a[0] = 123
    print(a)  # [123, 22, 33]
    print(b)  # [11, 22, 33]
    print(c)  # [11, 22, 33]

    从上面的输出结果看,当 a 变量修改之后,b 变量和 c 变量一样没有改变,那 copy() 和 deepcopy() 到底有什么区别呢,我们再看下面的代码:

    1 import copy
    2 
    3 a = [11, 22, [123,456]]
    4 b = copy.copy(a)
    5 c = copy.deepcopy(a)
    6 a[2][0] = 789
    7 print(a)  # [11, 22, [789, 456]]
    8 print(b)  # [11, 22, [789, 456]]
    9 print(c)  # [11, 22, [123, 456]]

    从上面的输出结果可以看出,b 变量随 a 变量的变化而变化了,而 c 变量没有。

    很显然这时 copy() 函数拷贝的值随着原对象的值修改了,而 deepcopy() 的值没有随着原对象的值修改。主要是因为 deepcopy()  会将复杂对象的每一层复制一个单独的个体出来对于 copy() 函数要慎用,慎用。

  • 相关阅读:
    [Unity]关于Physic Material,物理碰撞
    [Unity]当多个立方体堆叠,堆叠处出现缝隙的处理方法
    反射基础
    数据库三种事务
    一、手写ORM实现数据库查询
    UDP协议
    Wireshark抓包理解APR协议
    DBeaver用户界面窗口失效 查询窗口不显示
    二、IP、路由协议
    解决图片存入时 A generic error occurred in GDI+ 报错
  • 原文地址:https://www.cnblogs.com/weijiutao/p/10733851.html
Copyright © 2011-2022 走看看