Python可变对象和不可变对象 - Yabea - D1h.Net第一号博客
返回主页

Yabea

持续学习中...

随笔 - 52  文章 - 1  评论 - 91

Python可变对象和不可变对象

本文介绍了Python可变对象和不可变对象的原理,参数传递,深拷贝和浅拷贝。

Python中一切皆对象,每个对象都有其唯一的id,对应的类型和值,其中id指的是对象在内存中的位置。根据对象的值是否可修改分为可变对象和不可变对象。其中,

不可对象包括:数字,字符串,tuple

可变对象包括:list,dict,set

Python中的变量可以指向任意对象,可以将变量都看成是指针,保存了所指向对象的内存地址(对象的引用)。

不可变对象

对于不可变对象,如果要更新变量引用的不可变对象的值,会创建新的对象,改变对象的引用,举个例子:

In [41]: x = 1 In [42]: y = x In [43]: print(id(x)) 140719461487648 In [44]: x = 2 In [45]: print(id(y)) 140719461487648 In [46]: print(id(x)) 140719461487680 In [47]: print(id(2)) 140719461487680 

上述是int类型的一个实例,可以看到:

  1. 想要变量的值,会在内存中创建一个新的对象,变量指向新的对象。
  2. 对于值为1或者2,不管几个引用指向它,内存中都只占用了一个地址,在Python内部会通过引用计数来记录指向该地址的引用个数,当引用个数为0时会进行垃圾回收。

所以,不可变对象的优点是对于相同的对象,无论多少个引用,在内存中只占用一个地址,缺点是更新需要创建新的对象,因此效率不高。

可变对象

对于可变对象,举个例子:

In [57]: a = [1, 2] In [58]: b = a In [59]: print(id(a), id(b)) 1961088949320 1961088949320 In [60]: a.append(3) In [61]: print(a, b) [1, 2, 3] [1, 2, 3] In [62]: print(id(a), id(b)) 1961088949320 1961088949320 In [63]: a = [1, 2, 3] In [64]: print(id(a)) 1961088989704 

可以看到:

  1. 值的变化是在原有对象的基础上进行更新的,变量引用的地址没有变化。
  2. 对于一个变量的两次赋值操作,值相同,但是引用的地址是不同的,也就是同样值的对象,在内存中是保存了多份的,地址是不同的。

注意,我们研究可变对象的变化,研究的是同一对象,也就是可变指的是append, +=这种操作,而不包括新的赋值操作,赋值操作是会新建一个对象的。比如:

In [96]: a = [1, 2, 3] In [97]: b = a In [98]: a = [1] In [99]: b Out[99]: [1, 2, 3] 

参数传递问题

因为可变对象和不可变对象的特性,因此在参数传递上需要注意,详情可参考 我的回答

深拷贝和浅拷贝

首先,举个例子:

In [69]: data = [{'name': 'a', 'deleted': True}, {'name' : 'b', 'deleted': False}, {'name': 'c', 'deleted': False}] In [70]: print(data) [{'name': 'a', 'deleted': True}, {'name': 'b', 'deleted': False}, {'name': 'c', 'deleted': False}] In [71]: def add(data_list): ...: for item in data_list: ...: if item.get('deleted'): ...: data_list.remove(item) ...: return data_list ...: In [72]: add_result = add(data) In [73]: print(add_result) [{'name': 'b', 'deleted': False}, {'name': 'c', 'deleted': False}] In [74]: print(data) [{'name': 'b', 'deleted': False}, {'name': 'c', 'deleted': False}] 

你会发现调用了add方法之后,data已经变了,在之后的代码中你已经无法再使用原来的data了,具体的原因在参数传递那个问题中我有说明。

但是,当你希望在add方法中并不会修改data的值,要怎么做呢?

这时候,你需要了解下深拷贝和浅拷贝:

深拷贝和浅拷贝的概念:

  1. 浅拷贝(shallow copy):构造一个新的对象并将原对象中的引用插入到新对象中,只拷贝了对象的地址,而不对对应地址所指向的具体内容进行拷贝,也就是依然使用原对象的引用。实现方式包括:工厂函数(list, set等)、切片,copy模块的copy方法。
  2. 深拷贝(deep copy):复制了对象的和引用,深拷贝得到的对象和原对象是相互独立的。实现方式:copy模块的deepcopy方法。

所以,上述代码可按需更新为:

def add(data_list): ret_data_list = deepcopy(data_list) for item in ret_data_list: if item.get('deleted'): ret_data_list.remove(item) return ret_data_list 

以上。

posted @ 2020-05-24 17:22  Yabea  阅读(...)  评论(...编辑  收藏
Copyright © 2020 Yabea
Powered by .NET Core on Kubernetes

问答 28u iTmz.Net 3q科技 A8团队1 A8团队2 A8团队3 A8备