深入理解 python对象可变/不可变、传参与复制、深(浅)拷贝

深入认识python的对象

Python使用对象模型来储存数据,所以在python中一切皆对象

  • 对象有三个特性:
    • 身份标示:是唯一的,可以理解为对象的内存地址。 id()
    • 类型: type()

所以,任何变量都可以用id()和type()来查看其对象的特性 

  • 根据对象的值是只读还是可更新,把对象(即所有的python数据类型)分为可变类型(mutable)和不可变类型(immutable)
    • 不可变类型:所有数值类型、字符串、元组
    • 可变类型:列表、字典。
str = "abc"
str[0] = "d"

"""
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'str' object does not support item assignment
"""

tuple = (2,3)
tuple[0] = 4
"""
Traceback (most recent call last)
  File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
"""


复制代码

注意:python中有的对不可变对象进行的操作,看起来是变化了,实际上是生成了新对象。

str = "abc"
print(id(str)) # 4483428440

str = str + "d"
print(str,id(str)) # ('abcd', 4515702032)

str = str.replace("a","d")
print(str,id(str)) # ('dbcd', 4778928816)
复制代码

python的赋值和传参

  • 变量的赋值,只表示让变量指向了某个对象,不是让变量指向某个内存地址,也不是拷贝对象给变量。

"Remember that arguments are passed by assignment in Python"

  • 关于传参,准确地说,python中的参数传递就是赋值传递(passed by assignment)python所有数据类型都是对象,所以参数传递,只是让新变量与原变量指向相同的对象而已,并不存在值传递或饮用传递一说。

对python来说,不管是赋值还是传参,不是指向一个具体的内存地址,而是指向一个具体的对象。

  • 如果对象是可变的,那当其改变,所有指向它的变量都会改变

  • 如果对象是不可变的,那所有指向该对象的变量值总是一样的,如果通过操作“更新”不可变对象,本质上不是更新了,是生成了一个新的对象。

def my_func1(b):
    print("b:id_1:{}".format(id(b)))
    b = 2
    print("b:id_2:{}".format(id(b)))
    
a = 1
print("a:id_1:{}".format(id(a)))
my_func1(a)
print("a:{}".format(a))
print("a:id_2:{}".format(id(a)))

#a:id_1:140553278747896
#b:id_1:140553278747896
#b:id_2:140553278747872
#a:1
#a:id_2:140553278747896

复制代码
def my_func2(list_b):
    print("list_b:id_1:{}".format(id(list_b)))
    list_b.append(4)
    print("list_b:id_2:{}".format(id(list_b)))

#list_a = [1, 2, 3]
#print("list_a:id_1:{}".format(id(list_a)))
#my_func2(list_a)
#print(list_a)
#print("list_a:id_2:{}".format(id(list_a)))
复制代码

深复制(deep copy)和浅复制(shallow copy)

我们已经知道了,赋值和传参本质上是将变量指向原有的一个对象,那在python中,如果想生成一个新的对象,那就需要拷贝(复制)。深拷贝和浅拷贝都会创建新对象,而非原对象本身的引用。

  • 浅拷贝创建的新对象,是原对象内第一层对象的引用。
    • 浅拷贝有三种形式:切片操作、工厂函数、copy 模块中的 copy 函数。

      a = [1,2,3]
      切片操作:b=a[:] 或者 b=[xforxina];
      工厂函数:b = list(a);
      copy 函数:b = copy.copy(a);

    • 浅拷贝产生的列表 list_b 不再是列表 list_a 了,使用 is判断可以发现他们不是同一个对象,使用 id 查看,他们也不指向同一片内存空间。在这种情况下,列表 list_a 和 list_b是不同的对象,修改列表 list_b理论上不会影响到列表 list_a。

    • 但是要注意的是,浅拷贝之所以称之为浅拷贝,是它仅仅只拷贝了一层。但是当我们查看其中元 素的地址时,可以看到二者包含的元素的地址是相同的。我们修改了嵌套的list,修改外层元素,会修改它的引用,让它们指向别的位置,修改嵌套列表中的元素,列表的地址并未发生变化,指向的都是用一个位置

import copy
list_a = [[a,2],{"a":2}]
print("list_a'id:{}".format(id(list_a)))  # list_a'id:4780111992
for a in list_a:
    print("a'id:{}".format(id(a)))          # a'id:4779267928
                                            # a'id:4780054520

list_b = list_a[:]
print("list_b'id:{}".format(id(list_b)))  #list_b'id:4779229336
for b in list_b:
    print("b'id:{}".format(id(b)))         # b'id:4779267928
                                           # b'id:4780054520

list_a.append(3)
print(list_b)           # [[{'a': 2}, 2], {'a': 2}]
    
list_a[0].append(4)
print(list_b)           # [[{'a': 2}, 2, 4], {'a': 2}]

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

    • 同样的对列表 list_a,如果使用 list_b = copy.deepcopy(list_a),再修改列表 list_b 将不会影响到列表 list_a,即使嵌套的列表具有更深的层次,也不会产生任何影响,因为深拷贝拷贝出来的对象根本就是一个全新的对象, 不再与原来的对象有任何的关联。

import copy
list_a = [[a,2],{"a":2}]
print("list_a'id:{}".format(id(list_a)))  # list_a'id:4775350000
for a in list_a:
    print("a'id:{}".format(id(a)))      # a'id:4781058888
                                        # a'id:4780657472

list_b = copy.deepcopy(list_a)
print("list_b'id:{}".format(id(list_b)))  # list_b'id:4780511384
for b in list_b:
    print("b'id:{}".format(id(b)))      #b'id:4775453616
                                        #b'id:4775426600


list_a.append(3)    
print(list_b)           [[{'a': 2}, 2], {'a': 2}]
    
list_a[0].append(3)
print(list_b)           [[{'a': 2}, 2], {'a': 2}]
    
复制代码
  • 注意:不管是深拷贝,还是浅拷贝,都是对于对于可变类型来说的,对数字、字符、元组,这些不可变类型,没有拷贝一说,产生的变量都是原对象的引用。 如果元组变量值包含原子类型对象,即使采用了深拷贝,也只能得到浅拷贝。
import copy
# s = "aa"
# s = 123
s = (2,3,4)
print(id(s))  # 4774929104
v = copy.copy(s)
print(id(v))  # 4774929104
复制代码

转载于:https://juejin.im/post/5d078309f265da1bbf691dfa

猜你喜欢

转载自blog.csdn.net/weixin_34206899/article/details/93181734
今日推荐