• Python比较操作符、变量赋值、对象拷贝


    Python比较操作符、变量赋值、对象拷贝

    1. 比较操作符 == 和 is

    1.1 区别
    • == 操作符比较对象之间的值是否相等
    • is 操作符比较的是对象的身份标识是否相等,即是否是同一个对象,是否指向同一个内存地址
    • is 操作符的速度效率通常要优于==,因为is操作符不能被重载,执行is操作只是简单的获取对象的ID,并进行比较,而等于操作符则会递归地遍历对象所有值,并逐一比较
    • 当比较一个变量与一个单例时,通常使用is
    1.2 实例
    # 比较两个对象
    def compare(A, B):
        if A == B:
            print(f"{A} == {B}:{True}")
        else:
            print(f"{A} == {B}:{False}")
        if A is B:
            print(f"{A} is {B}:{True}")
        else:
            print(f"{A} is {B}:{False}")
            print(id(A), id(B))
    
    A = -7
    B = -7
    compare(A, B)
    C = 4
    D = 4
    compare(C, D)
    
    """
    python内部对**-5到256的整型**维持一个数组,起到一个缓存的作用,使得性能优化,因此,在-5到256之间的整型数字比较,都相等。上述代码是在jupyter notebook中运行的,如果在pycharm中运行,则都是True,pycharm中做了优化。
    """
    -7 == -7:True
    -7 is -7:False
    139667038587504 139667038587152
    4 == 4:True
    4 is 4:True
    
    # 比较一个变量
    if a is None:
          ...
    
    if a is not None:
          ...
    

    2. 变量及其赋值

    2.1 概念和逻辑关系
    • 变量的赋值,只是表示让变量指向了某个对象,并不表示拷贝对象给变量;而一个对象,可以被多个变量所指向。

    • 可变对象(列表,字典,集合等等)的改变,会影响所有指向该对象的变量。

    • 对于不可变对象(字符串、整型、元组等等),所有指向该对象的变量的值总是一样的,也不会改变。但是通过某些操作(+= 等等)更新不可变对象的值时,会返回一个新的对象

    • 变量可以被删除,但是对象无法被删除,需要通过python垃圾回收机制回收。

    2.2 Python函数的参数传递

    ​ 是赋值传递,python里所有的数据类型都是对象,所以参数传递时,只是让让新变量与原变量指向相同的对象而已,并不存在值传递或是引用传递(c++等语言中)一说。

    2.3 思考题

    2.3.1 变量指向的是同一个对象吗?——查id

    # l1,l2,分别分配了内存空间,不是指向同一个对象
    # l2,l3,是指向
    l1 = [1, 2, 3] # 创建了新对象
    l2 = [1, 2, 3] # 创建了新对象
    l3 = l2  # 指向同一个对象
    print(id(l1), id(l2), id(l3))
    # 1479611736648 1479611736712 1479611736712
    

    2.3.2 变量被修改了吗?

    # 字典是可变对象,对象改变,会影响所有指向该对象的变量
    def func(d):
        d['a'] = 10
        d['b'] = 20
    d = {'a': 1, 'b': 2}
    func(d)
    print(d) # {'a': 10, 'b': 20}
    

    3. 浅拷贝和深度拷贝

    3.1 浅拷贝概念

    浅拷贝是指重新分配一块内存,创建一个新的对象,里面的元素是原对象内第一层对象的引用,因此,如果原对象中的元素是可变的,改变其也会影响拷贝后的对象,存在一定的副作用。

    3.2 浅拷贝方法——可变对象
    • 类型工厂函数:
      • 是指不通过类而是通过函数来创建对象,list(),set(),int(),dict(),tuple(),str()等数据类型本身的构造器
      • 浅拷贝中适用的是可变对象,因此,list(),set(),dict()适用,其余不适用
    • 切片操作:列表
    • copy模块中copy方法:copy.copy()
    3.3 深度拷贝概念

    深度拷贝是指重新分配一块内存,创建一个新的对象,并且将元对象中的元素,以递归的方式,通过创建新的子对象拷贝到新对象中,因此,新对象和原对象没有任何关联。另外,深度拷贝中会维护一个字典,记录已经拷贝的对象及其ID,来提高效率并放置无限递归的发生。

    3.4 深度拷贝方法
    • copy模块中的deepcopy方法:copy.deepcopy()
    3.5 实例
    3.5.1 浅拷贝和赋值的区别——是否会创建一个新对象
    l1 = [1, 2, 3]
    l2 = list(l1) # 使用工厂函数实现浅拷贝
    compare(l1, l2)
    a = [1, 2, 3]
    b = a  # 变量赋值
    compare(a, b)
    
    [1, 2, 3] == [1, 2, 3]:True
    [1, 2, 3] is [1, 2, 3]:False
    140603550652928 140603550660720
    [1, 2, 3] == [1, 2, 3]:True
    [1, 2, 3] is [1, 2, 3]:True
    
    3.5.2 字符串、数字不能实现拷贝
    import copy
    t1 = 4
    t2 = copy.deepcopy(t1) # 对一个元组实现深度拷贝
    compare(t1, t2)
    sr1 = "adc"
    sr2 = str(sr1) # 字符串不能创建浅拷贝
    compare(sr1, sr2)
    
    4 == 4:True
    4 is 4:True
    adc == adc:True
    adc is adc:True
    
    3.5.3 元组的浅拷贝和深度拷贝
    import copy
    s1 = (1, 2, 3,[1,2])
    s2 = copy.copy(s1) # 元组不能实现浅拷贝
    compare(s1, s2)python
    
    (1, 2, 3, [1, 2]) == (1, 2, 3, [1, 2]):True
    (1, 2, 3, [1, 2]) is (1, 2, 3, [1, 2]):True
    
    import copy
    t1 = (1, 2, 3)
    t2 = copy.deepcopy(t1) # 只包含不可变对象的元组不能实现深拷贝
    compare(t1, t2)
    
    (1, 2, 3) == (1, 2, 3):True
    (1, 2, 3) is (1, 2, 3):True
    
    import copy
    s1 = (1, 2, 3,[1,2])
    s2 = copy.deepcopy(s1) # 对一个包含可变对象的元组可以实现深度拷贝
    compare(s1, s2)
    
    (1, 2, 3, [1, 2]) == (1, 2, 3, [1, 2]):True
    (1, 2, 3, [1, 2]) is (1, 2, 3, [1, 2]):False
    139823086708208 139823086707728
    
    3.5.4 浅拷贝和深度拷贝的影响
    # 浅拷贝:原对象的改变可能会影响新对象
    import copy
    l1 = [[1, 2], (30, 40)]
    print(f"原对象:{l1}")
    l2 = copy.copy(l1)
    l1.append(100)
    l1[0].append(3)
    print(f"原对象修改:{l1}")
    print(f"浅拷贝后的新对象:{l2}")
    
    原对象:[[1, 2], (30, 40)]
    原对象修改:[[1, 2, 3], (30, 40), 100]
    浅拷贝后的新对象:[[1, 2, 3], (30, 40)]
    
    # 深度拷贝:新对象和原对象没有任何关联
    import copy
    l1 = [[1, 2], (30, 40)]
    print(f"原对象:{l1}")
    l2 = copy.deepcopy(l1)
    l1.append(100)
    l1[0].append(3)
    print(f"原对象修改:{l1}")
    print(f"深度拷贝后的新对象:{l2}")
    
    原对象:[[1, 2], (30, 40)]
    原对象修改:[[1, 2, 3], (30, 40), 100]
    深度拷贝后的新对象:[[1, 2], (30, 40)]
    
    3.5.6 用一个深度拷贝,拷贝一个无限嵌套的列表,是否相等
    import copy
    x = [1]
    x.append(x)
    y = copy.deepcopy(x)
    print(len(y)) # 输出为2
    if x == y:
        print(True)
    else:
        print(False)
    # 运行报错:RecursionError: maximum recursion depth exceeded in comparison
    

    总结

    一、赋值:

    在 Python 中,对象的赋值就是简单的对象引用,这点和 C++不同

    二、浅拷贝(shallow copy):

    浅拷贝会创建新对象,重新分配内存,其内容非原对象本身的引用,而是原对象内第一层对象的引用。浅拷贝有三种形式:切片操作工厂函数copy 模块中的 copy 函数

    三、深拷贝(deep copy):

    深拷贝只有一种形式,copy 模块中的 deepcopy()函数。深拷贝和浅拷贝对应,深拷贝拷贝了对象的所有元素,包括多层嵌套的元素。因此,它的时间和空间开销要高。

    四、拷贝的注意点:

    1、对于非容器类型,如数字、字符,以及其他的“原子”类型,没有拷贝一说,产生的都是原对象的引用
    2、如果元组变量值包含原子类型对象,即使采用了深拷贝,也只能得到浅拷贝。

    写在最后:
    吐血整理,不过好在是终于理清了,文章中所有的例子可以在jupyter nootbook上运行,转载请注明出处,谢谢!!

  • 相关阅读:
    openssl-1.0.2s window下编译,生成dll
    webrtc中Native层视频解码器的创建过程
    webrtc收发音视频(createoffer的使用)
    webrtc中 video/audioTrack 架构解析
    让自己的vs工程能够使用webrtc库
    如何修改webrtc的工程(vs 2017)
    webrtc在windows下的编译
    ffserver在windows下的编译
    网络学习笔记(三):HTTP缓存
    Vue2.0源码阅读笔记(四):nextTick
  • 原文地址:https://www.cnblogs.com/donghe123/p/13278354.html
Copyright © 2020-2023  润新知