设为首页 加入收藏

TOP

Python常见面试题001-005,涉及深浅拷贝、MRO、函数可变参数、作用域、is和==的区别等(一)
2023-07-25 21:24:31 】 浏览:68
Tags:Python 常见面 001-005 MRO 别等

Python常见面试题001-005

参考资料

https://github.com/taizilongxu/interview_python

https://github.com/hantmac/Python-Interview-Customs-Collection

https://github.com/kenwoodjw/python_interview_question

有些来自上面(但我也做了自己的补充),有些来自网络或书籍

本文不准备写编程题,偏重于理论一些。你要的话去刷leetcode就是了。
倒序描述,限于篇幅,可能要连载

005. 说说你对浅拷贝、深拷贝的理解

  • 浅拷贝 shallow copy
  • 深拷贝 deep copy

浅拷贝

  • 第一次见浅拷贝是在

    >>> help(list.copy)
    Help on method_descriptor:
    
    copy(self, /)
        Return a shallow copy of the list.
    
  • list列表的copy方法,那浅拷贝到底是怎样的行为表现呢?

  • 示例代码

    list1 = [1,2,3]
    list2 = list1.copy()
    list1.append(4)
    print(list2)  # [1,2,3] list1改变了,list2没变 , 浅拷贝是互相不会影响的
    
  • 来看这段

    list1 = [1,2,3]
    list2 = list1
    list1.append(4)
    print(list2)  # [1,2,3,4]  ,赋值是引用,同时指向了一个内存区域,现在你改变了内存的信息,自然一起改变了
    
  • 在第一个例子中

    list1 = [1,2,3]
    list2 = list1.copy()
    print(id(list1))
    print(id(list2))  # 显然2个id的值是不一样的,所以改变了你,并不会改变我
    
    print(list1 == list2)  # True 
    print(list1 is list2)   # False
    

  • 来看看其他的浅拷贝方式

切片

  • 可变序列的切片创建了一个浅拷贝,不可变序列的切片创建了一个引用

    list1 = [1,2,3]
    list2 = list1[:]
    list1.append(4)
    print(list2)   # [1,2,3] 跟list1不一样
    print(list2 is list1) # False
    tuple1 = (1,2,3)
    tuple2 = tuple1[:]
    print(tuple1 is tuple2)  # True
    

  • 浅拷贝,是指重新分配一块内存,创建一个新的对象,里 面的元素是原对象中子对象的引用。因此,如果原对象中的元素不可变,那倒无所谓;但如果元 素可变,浅拷贝通常会带来一些副作用

构造器

  • 常见的浅拷贝的方法,是使用数据类型本身的构造器

    set1 = {1,2,3}
    set2 = set(set1)
    print(set2 is set1)  # False
    

copy.copy()

  • copy.copy则可以用于任意的数据类型的浅拷贝

    >>> from copy import copy
    >>> help(copy)
    Help on function copy in module copy:
    
    copy(x)
        Shallow copy operation on arbitrary Python objects.
    
        See the module's __doc__ string for more info.
    
  • 示例代码

    dict1 = {"name":"wuxianfeng","age":18}
    from copy import copy
    dict2 = copy(dict1)
    print(dict2 is dict1)
    dict1['height'] = 180
    print(dict2)
    

特殊情况

  • 来看下面的这些例子

    l1 = [[1, 2], (30, 40)]
    l2 = list(l1)
    l1.append(100)
    l1[0].append(3)
    print(l2)  # 请问此时l2是什么?
    
  • 按照前面的理解,list构造器会产生浅拷贝

    print(l2 is l1)  # False  # 说明这是2个不同的对象
    
  • 按理说l1的变化不会影响到l2

  • 但事实是影响了

    [[1, 2, 3], (30, 40)]  # 第四行代码发生作用了
    
  • 仔细看会发现,line3没有改变,line4改变了

  • 再看一个操作

    # 承上
    l1[1] +=(50,60)
    print(l2)  # 还是[[1, 2, 3], (30, 40)]  但l1肯定是变了的
    
  • 完整的解释

    l1 = [[1, 2], (30, 40)]
    l2 = list(l1)  # 对l1执行浅拷贝,赋予l2。
    # 因为浅拷贝里的元素是对原对象元素的引用,因此l2中的元素和l1中的元素指向同一个列表和元组对象
    
    l1.append(100)
    # 这个操作不会对l2产生任何影响,因为l2和l1作为整体是两个不同的对象,并不共享内存地址
    
    l1[0].append(3)
    # l1[0] 是[1, 2]
    # 因为l1和l2的[1, 2]是同一个对象,不信?
    # print(id(l1[0]))
    # print(id(l2[0]))
    # print(l1[0] is l2[0]) # True
    # 所以l1[0].append(3) 这步会对l1和l2的[1,2]都追加一个元素3
    
    l1[1] +=(50,60)
    # 最麻烦的是这个
    # l1[1] 是(30,40) ,是个元组
    # l2[1] 的确是同一个对象
    # l1[1] +=(50,60) 会创建一个新的,注意是新的!元组
    # 但l2不会
    
  • 你可以这样验证

    print('l1的元组的id',id(l1[1]),'l1的元组的id',id(l2[1]))
    l1[1] +=(50,60)
    print('l1的元组的id',id(l1[1]),'l1的元组的id',id(l2[1]))
    
    
    # 输出参考
    l1的元组的id 2411404337920 l1的元组的id 2411404337920
    l1的元组的id 2411404749456 l1的元组的id 2411404337920
    # 是的,l1中的元组已经变了,l2的没有改变
    
  • 至此你应该发现了浅拷贝的一些副作用

深拷贝

  • 深拷贝来自copy模块的deepcopy方法

  • 同样看上面的例子

    from copy import deepcopy
    l1 = [[1, 2], (30, 40)]
    l2 = deepcopy(l1)
    l1.append(100)
    l1[0].append(3)
    l1[1] +=(50,60)
    print(l2)   # [[1, 2], (30, 40)] # 就是你复制的时候的样子
    
  • 好像deepcopy很完美?

  • 复制的时候的确不希望互相影响

  • 但deepcopy有它的弊端:如果被拷贝对象中存在指向自身的引 用,那么程序很容易陷入无限循环

官方解释

https://docs.python.org/zh-cn/3.9/library/copy.html

  • Python 的赋值语句不复制对象,而是创建目标和对象的绑定关系

  • 对于自身可变,或包含可变项的集合,有时要生成副本用于改变操作,而不必改变原始对象。本模块提供了通用的浅层复制和深层复制操作

    • copy.copy(x) 浅
首页 上一页 1 2 3 4 下一页 尾页 1/4/4
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇selenium webdriver 实例化对象的.. 下一篇【爬虫+数据清洗+可视化分析】舆..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目