Python可变对象和不可变对象
时间:2022-07-24
本文章向大家介绍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,不管几个引用指向它,内存中都只占用了一个地址,在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
可以看到:
- 值的变化是在原有对象的基础上进行更新的,变量引用的地址没有变化。
- 对于一个变量的两次赋值操作,值相同,但是引用的地址是不同的,也就是同样值的对象,在内存中是保存了多份的,地址是不同的。
注意,我们研究可变对象的变化,研究的是同一对象,也就是可变指的是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的值,要怎么做呢?
这时候,你需要了解下深拷贝和浅拷贝:
深拷贝和浅拷贝的概念:
- 浅拷贝(shallow copy):构造一个新的对象并将原对象中的引用插入到新对象中,只拷贝了对象的地址,而不对对应地址所指向的具体内容进行拷贝,也就是依然使用原对象的引用。实现方式包括:工厂函数(list, set等)、切片,copy模块的copy方法。
- 深拷贝(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
以上。
- spring: 加载远程配置
- java:如何让程序按要求自行重启?
- Angular学习-指令入门
- java:如何让程序按要求自行重启?
- linux:nohup 不生成 nohup.out的方法
- 让VIM支持Python2 by update-alternatives
- Angular中ngCookies模块介绍
- 如何让jboss eap 6.2+ 的多个war应用共享 jar 包?
- scala 学习笔记(07) 一等公民的函数
- 让BASH,VIM美美的Powerline
- 极品三数666.cn易主,价格让人大惊!
- ui-router中使用ocLazyLoad和resolve
- rpc框架之 avro 学习 2 - 高效的序列化
- rpc框架之HA/负载均衡构架设计
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Laravel5.1 框架关联模型之后操作实例分析
- PHP实现的多进程控制demo示例
- 你可能不知道的一些linux文件权限管理办法
- Laravel框架路由与MVC实例详解
- python实现学生管理系统开发
- 使用 chkconfig 和 systemctl 命令启用或禁用 Linux 服务的办法
- PHP defined()函数的使用图文详解
- 详解在Linux中清空或删除大文件内容的5种办法
- php和redis实现秒杀活动的流程
- CentOS 7中搭建NFS文件共享存储服务的完整步骤
- PHP检测一个数组有没有定义的方法步骤
- Yii2.0框架模型多表关联查询示例
- Yii2.0框架模型添加/修改/删除数据操作示例
- thinkphp5.1框架容器与依赖注入实例分析
- 在CentOS VPS上通过SSH安装 MySQL的办法图解