美文网首页
python3 参数中的陷阱,可变对象和不可变对象

python3 参数中的陷阱,可变对象和不可变对象

作者: tafanfly | 来源:发表于2019-12-19 15:30 被阅读0次

    介绍引用传递

    python只允许使用引用传递, 不存在其他语言中的值传递。引用传递即引用内存地址, 无论是赋值还是函数调用。

    不可变对象

    不可变对象指向内存地址的值不能够被改变, 这些对象包含int,string,float,tuple

    i = 1
    j = i
    
    print('id is %s for i' % id(i))
    print('id is %s for j' % id(j))
    print(i, j)
    
    j = j + 1
    
    print('id is %s for i' % id(i))
    print('id is %s for j' % id(j))
    print(i, j)
    #result
    id is 11061440 for i
    id is 11061440 for j
    1 1
    id is 11061440 for i
    id is 11061472 for j
    1 2
    
    • 最初 i 赋值给 j 时是引用传递, 它们的内存地址都是11061440, 即 i 和 j 指向同一个内存地址
    • 后面当对象 j 被改变时, 由于所指向的内存中的值不能被改变, 所以会复制一份值到新的地址再处理计算(+1), 然后对象 j 指向新的地址11061472
      不可变对象

    可变对象

    可变对象指向内存地址的值直接被改变, 这些对象包含list,dict,set

    a = [1]
    b = a
    
    print('id is %s for a' % id(a))
    print('id is %s for b' % id(b))
    print(a, b)
    
    b[0] = 0
    
    print('id is %s for a' % id(a))
    print('id is %s for b' % id(b))
    print(a, b)
    #result
    id is 140198854737480 for a
    id is 140198854737480 for b
    [1] [1]
    id is 140198854737480 for a
    id is 140198854737480 for b
    [0] [0]
    
    • 最初 a 赋值给 b 时是引用传递, 它们的内存地址都是140198854737480, 即 a 和 b 指向同一个内存地址
    • 后面当对象 b 被改变时, 由于所指向的内存中的值直接被改变, 发现改变b的同时a也被改变了, 但是 a 和 b 还是指向同一个内存地址
      可变对象

    函数的参数传递也分可变和不可变对象的

    def test_para(a, b):
        a += 1
        b[0] = 0
        print('========1111111111==============')
        print('a is %s and the id is %s.' % (a, id(a)))
        print('b is %s and the id is %s.' % (b, id(b)))
    
    a = 1
    b = [1]
    print('a is %s and the id is %s.' % (a, id(a)))
    print('b is %s and the id is %s.' % (b, id(b)))
    test_para(a, b)
    print('========22222222222==============')
    print('a is %s and the id is %s.' % (a, id(a)))
    print('b is %s and the id is %s.' % (b, id(b)))
    
    #result
    a is 1 and the id is 11061440.
    b is [1] and the id is 139909172055624.
    ========1111111111==============
    a is 2 and the id is 11061472.
    b is [0] and the id is 139909172055624.
    ========22222222222==============
    a is 1 and the id is 11061440.
    b is [0] and the id is 139909172055624.
    
    • 当传过来的是可变对象,函数内部修改会影响函数外部的可变对象。
    • 当传过来的是不可变对象,函数内部修改不会影响函数外部的不可变对象,因为修改的时候会先复制一份再修改
    def test_para(b=[]):
        b += [0]
        print('b is %s and the id is %s.' % (b, id(b)))
    
    test_para()
    test_para()
    test_para()
    test_para([1, 2, 3])
    
    #result
    b is [0] and the id is 140394154311240.
    b is [0, 0] and the id is 140394154311240.
    b is [0, 0, 0] and the id is 140394154311240.
    b is [1, 2, 3, 0] and the id is 140394177812936.
    
    • Python 解释器执行 def 语句时, 会创建一个函数对象, 且只会有一个
    • 默认参数 b 就已经计算出来, 指向了 [] 空列表
    • 第一次调用test_para()时, 往b中添加一个值为1的元素
    • 第二次,第三次调用test_para()时, 继续往b中添加一个值为1的元素, 用了同一个对象b, 指向同一个内存地址
    • 当调用test_para([1, 2, 3])时, b 被重新赋值, b指向了[1, 2, 3]的新地址

    list中的注意事项

    • 对象被重新赋值
      a = a + [3] 或者 a = [3] 都是重新被赋值, 但是 a += [3]不是的,相当于a.extend([3])
    a = b = [1, 2]
    print(a, id(a), b, id(b))
    
    a = a + [3]
    print(a, id(a), b, id(b))
    #result
    [1, 2] 140607382289992 [1, 2] 140607382289992
    [1, 2, 3] 140607405791752 [1, 2] 140607382289992
    
    a = b = [1, 2]
    print(a, id(a), b, id(b))
    
    a += [3]
    print(a, id(a), b, id(b))
    #result
    [1, 2] 140479908183624 [1, 2] 140479908183624
    [1, 2, 3] 140479908183624 [1, 2, 3] 140479908183624
    
    a = b = [1, 2]
    print(a, id(a), b, id(b))
    
    a = [3]
    print(a, id(a), b, id(b))
    #result
    [1, 2] 139779074858568 [1, 2] 139779074858568
    [3] 139779098360264 [1, 2] 139779074858568
    
    a = [1, 2]
    s = [a] * 2
    print(s, id(s[0]), id(s[1]))
    a.append(3)
    print(s, id(s[0]), id(s[1]))
    a[0] = 0
    print(s, id(s[0]), id(s[1]))
    s[0] = [0]
    print(s, id(s[0]), id(s[1]))
    #result
    [[1, 2], [1, 2]] 139773557825096 139773557825096
    [[1, 2, 3], [1, 2, 3]] 139773557825096 139773557825096
    [[0, 2, 3], [0, 2, 3]] 139773557825096 139773557825096
    [[0], [0, 2, 3]] 139773581326792 139773557825096
    
    • 如何复制list的值,但不指向同一个地址
      一阶列表高阶列表有很大区别, 一阶列表可以使用a[:], list(a), a.copy(), copy.copy(a), copy.deepcopy(a) 等方法拷贝一份, 而高阶列表只能使用copy.deepcopy(a)深度拷贝一份
    import copy
    
    a = [0, 1]
    b = 3
    num = [a, b]
    print('num is %s and id is %s\n' % (num, id(num)))
    
    c = num[:]
    d = list(num)
    e = num.copy()
    f = copy.copy(num)
    g = copy.deepcopy(num)
    
    print('c is %s and id is %s\n' % (c, id(c)),
          'd is %s and id is %s\n' % (d, id(d)),
          'e is %s and id is %s\n' % (e, id(e)),
          'f is %s and id is %s\n' % (f, id(f)),
          'g is %s and id is %s\n' % (g, id(g))
          )
    print('-------------------------------------------')
    a.append(2)
    print('num is %s and id is %s\n' % (num, id(num)))
    print('c is %s and id is %s\n' % (c, id(c)),
          'd is %s and id is %s\n' % (d, id(d)),
          'e is %s and id is %s\n' % (e, id(e)),
          'f is %s and id is %s\n' % (f, id(f)),
          'g is %s and id is %s\n' % (g, id(g))
          )
    
    #result
    num is [[0, 1], 3] and id is 140319229340360
    
    c is [[0, 1], 3] and id is 140319229131848
     d is [[0, 1], 3] and id is 140319229219464
     e is [[0, 1], 3] and id is 140319229219016
     f is [[0, 1], 3] and id is 140319229219784
     g is [[0, 1], 3] and id is 140319229220232
    
    -------------------------------------------
    num is [[0, 1, 2], 3] and id is 140319229340360
    
    c is [[0, 1, 2], 3] and id is 140319229131848
     d is [[0, 1, 2], 3] and id is 140319229219464
     e is [[0, 1, 2], 3] and id is 140319229219016
     f is [[0, 1, 2], 3] and id is 140319229219784
     g is [[0, 1], 3] and id is 140319229220232
    
    
    

    相关文章

      网友评论

          本文标题:python3 参数中的陷阱,可变对象和不可变对象

          本文链接:https://www.haomeiwen.com/subject/frnmnctx.html