zoukankan      html  css  js  c++  java
  • 【python】面试高频:浅拷贝 vs 深拷贝、'==' vs 'is'

    关于python在面试中会被问到哪些知识点,其实这主要取决于面试官。

    额,别拿刀先,马上扯正题。

    从我遇到的问题当中来看,除了有少数的常见问题外,更多的还是平时需要你额外拓展学习了解的知识点:

    • 常见问题:比如,可变/不可变数据类型、json的序列化和反序列化、对象的引用/作用域,字典的常用操作等等。
    • 额外知识:比如,python的内存管理机制、浅拷贝 vs 深拷贝、'==' vs 'is'等等。

    面试官为什么会问一些额外知识,我觉得还是在于考察候选人对语言了解的深度如何。如果你能静下心来去看一些python语言的书籍(我是属于没静下心的),回答这些问题基本上都不是什么问题。

    我现在也在翻阅几年前就买来的python书籍,有类似这种值得分享的知识点,在后续会一一带来。

    今天就先聊聊,我觉得面试出现频率最高的:浅拷贝 vs 深拷贝、'==' vs 'is'

    一、'==' vs 'is'

    为什么先聊这个,因为在后面的拷贝知识点中,会用到对象的比较。

    首先,先抛出知识点:

    • '=='操作符比较对象之间的是否相等。
    • 'is'操作符比较的是对象的身份标识是否相等,即它们是否是同一个对象,是否指向同一个内存地址

    注意

    • ==操作符是用来比较对象之间的是否相等。

    看示例(命令行交互下运行python):

    λ python
    >>> a = 10
    >>> b = 10
    >>> a == b
    True
    >>> id(a)
    2407440149072
    >>> id(b)
    2407440149072
    >>> a is b
    True
    >>>
    
    • 可以看到上面有2个变量 a 和 b,它们的值都是 10。
    • 在 Python 中,每个对象的身份标识,都能通过函数 id(object) 获得,所以 使用is,相当于比较对象之间的 ID 是否相等。

    在交互运行的结果中,可以看到:

    • a == b,结果是 True。
    • a is b,结果也是 True。

    嗯?那这俩效果不是一样嘛?

    分析一下:

    1. 首先 Python 会为 10 这个值开辟一块内存。
    2. 变量 a 和 b 同时指向这块内存区域。

    所以,因此 a 和 b 的值相等,id 也相等。

    但是,对于整型数字,a is bTrue,只适用于 -5 到 256 范围内的数字。
    现在用 257 试下:

    >>> a = 257
    >>> b = 257
    >>> a is b
    False
    

    看到结果为 False。

    原因在于,Python 出于对性能优化的考虑,内部会对 -5 到 256 的整型维持一个数组,起到一个缓存的作用。当你每次试图创建一个 -5 到 256 范围内的整型数字时,Python 都会从这个数组中返回相对应的引用,而不是重新开辟一块新的内存空间。

    但是,如果整型数字超过了这个范围,比如上述例子中的 257,Python 则会为两个 257 开辟两块内存区域,因此 a 和 b 的 ID 不一样,a is b就会返回 False 了。

    通常情况
    我们在使用中,还是用==更多,因为我们更关心两个变量的值,而不是它们内部的存储地址。

    不过当判断这个变量是不是为None的时候,通常会使用is了:

    if a is None:
        ...
    
    if a is not None:
        ...
    

    二、浅拷贝 vs 深拷贝

    深浅先不说,拷贝大家应该都会用到过。

    >>> a = [1, 2, 3]
    >>> b = list(a)
    >>> b
    [1, 2, 3]
    >>> a == b
    True
    >>> a is b
    False
    

    这里的 b 就是 a 的浅拷贝了,当然你也可以用列表的切片来处理:b = a[:]

    还可以用python 内部提供的函数copy来处理。

    >>> import copy
    >>> a = [1, 2, 3]
    >>> b = copy.copy(a)
    >>> b
    [1, 2, 3]
    >>> a is b
    False
    

    1. 浅拷贝

    浅拷贝:是指重新分配一块内存,创建一个新的对象,里面的元素是原对象中子对象的引用。

    怎么讲?

    来看一个新的示例:

    >>> import copy
    >>> a = [1, 2, 3, 4, ['a', 'b']]
    >>> b = copy.copy(a)
    >>> a
    [1, 2, 3, 4, ['a', 'b']]
    >>> b
    [1, 2, 3, 4, ['a', 'b']]
    >>> a is b
    False
    
    1. a 是一个列表,列表里面又存放了 5 个元素,其中第5个元素也是一个列表 ['a', 'b']。
    2. b 是 a 的浅拷贝,所以打印出来 a 和 b 的值都相等。
    3. a is b 是 False,说明是2个不同的对象。

    注意
    “新的对象,里面的元素是原对象中子对象的引用”,这里的子对象的引用,是怎么回事?

    现在我去修改 a,增加一个元素,应该是不会影响到 b。

    >>> a.append(5)
    >>> a
    [1, 2, 3, 4, ['a', 'b'], 5]
    >>> b
    [1, 2, 3, 4, ['a', 'b']]
    

    但是,如果我去修改列表 a 中的子对象 ['a', 'b'],增加一个元素 'c',再去看下 a,b 分别是什么:

    >>> a[4].append('c')
    >>> a
    [1, 2, 3, 4, ['a', 'b', 'c'], 5]
    >>> b
    [1, 2, 3, 4, ['a', 'b', 'c']]
    

    可以看到,b 受到影响了,b 的子对象列表也从原来的 ['a', 'b'] 变成了现在的 ['a', 'b', 'c']。

    所以这也就解释了上述的,“浅拷贝,是指重新分配一块内存,创建一个新的对象,里面的元素是原对象中子对象的引用”

    这也是浅拷贝的问题所在,在日常使用过程中,需要注意的地方。

    2. 深拷贝

    看到这,聪明的你或许已经明白深拷贝的作用了。没错,就是解决了浅拷贝的问题,实现真正的全拷贝。

    深拷贝:是指重新分配一块内存,创建一个新的对象,并且将原对象中的元素,以递归的方式,通过创建新的子对象拷贝到新对象中。

    因此,新对象和原对象没有任何关联

    再重新演示下上面的示例,用深拷贝copy.deepcopy()

    >>> import copy
    >>> a = [1, 2, 3, 4, ['a', 'b']]
    >>> b = copy.deepcopy(a)
    >>> a
    [1, 2, 3, 4, ['a', 'b']]
    >>> b
    [1, 2, 3, 4, ['a', 'b']]
    >>> a[4].append('c')
    >>> a
    [1, 2, 3, 4, ['a', 'b', 'c']]
    >>> b
    [1, 2, 3, 4, ['a', 'b']]
    

    用了深拷贝后,a 就算修改了子对象,b 也没有受到影响了。

    --不要用肉体的勤奋,去掩盖思考的懒惰--
  • 相关阅读:
    RequireJS 和 Sea.js
    zoom:1
    font-sqirrel
    WEB前端面试题 分别使用2个、3个、5个DIV画出一个大的红十字
    获取表单select域的选择部分的文本
    写一个简单的form表单,当光标离开表单的时候表单的值发送给后台
    Python3基础 e记法示例
    Python3基础 response.read 输出网页的源代码
    Python3基础 访问在线的有道词典
    Python3基础 response.info 服务器返回的header信息
  • 原文地址:https://www.cnblogs.com/pingguo-softwaretesting/p/14980059.html
Copyright © 2011-2022 走看看