Python中可变和不可变对象的深入讲解
作者:小菠萝测试笔记 发布时间:2022-12-29 21:31:21
目录
前置知识
有哪些可变对象,哪些不可变对象?
不可变对象和可变对象的区别?
不可变对象的应用场景
从内存角度出发说下有什么区别?
不可变对象
可变对象
从代码角度看看区别
不可变对象-整型
不可变对象-字符串
不可变对象-元组
可变对象列表
Python 函数的参数传递
概念
参数传递不可变对象
参数传递可变对象
总结
前置知识
在 Python 中,一切皆为对象
Python 中不存在值传递,一切传递的都是对象的引用,也可以认为是传址
有哪些可变对象,哪些不可变对象?
不可变对象:字符串、元组、数字(int、float)
可变对象:数组、字典、集合
不可变对象和可变对象的区别?
可变对象:改变对象内容,对象在内存中的地址不会被改变
不可变对象:改变对象内容,对象在内存中的地址会被改变;如果必须存储一个不同的值,则必须创建新的对象
不可变对象的应用场景
它们在需要常量哈希值的地方起着重要作用,例如作为字典中的键
从内存角度出发说下有什么区别?
不可变对象
Python 中的变量有一个内存空间
具体的数据(对象)也有一个内存空间
而变量保存(指向)的是存储数据(对象)的内存地址,一般也叫对象引用
不可变对象是指对象内容本身不可变
变的是:改变了值,会创建新对象,然后变量改变了对象引用,指向了新对象,旧对象会被垃圾回收
可变对象
变的是:原来对象的内容,不会创建新对象,而变量也还是指向原对象
从代码角度看看区别
不可变对象-整型
a = 123
b = a
print(id(a))
print(id(b))
print(a, b)
a += 2
print(id(a))
print(id(b))
print(a, b)
# 输出结果
4473956912
4473956912
123 123
4473956976
4473956912
125 123
从前两次打印可以看到,a、b 变量保存的内存地址是同一个,他们们都保存了 123 的内存地址(123 对象的引用)
预期情况:在 a 做了加法赋值运算之后,既然他们一开始都是指向同一个内存地址,按道理修改 123 后,他们也应该仍然指向同一个内存地址呀,但是并没有!
实际情况:a 指向了新的内存地址,而 b 仍然指向旧的内存地址,所以他们的值也不一样
可以看看下面的图
首先,这是一个内存区域
原理
因为数字(int、float) 是不可变对象,所以不能在 123 的内存地址上直接修改数据
加法赋值,实际上是将原来的 123 复制了一份到新的内存地址,然后再做加法,得到一个新的值 125,最后 a 再指向新的内存地址
不可变对象-字符串
a = "test"
b = a
print(id(a))
print(id(b))
print(a, b)
a += "123"
print(id(a))
print(id(b))
print(a, b)
# 输出结果
4455345392
4455345392
test test
4455818288
4455345392
test123 test
不可变对象-元组
a = (1, 2, 3)
b = a
print(id(a))
print(id(b))
print(a, b)
a = a + a
print(id(a))
print(id(b))
print(a, b)
# 输出结果
4455410240
4455410240
(1, 2, 3) (1, 2, 3)
4455359200
4455410240
(1, 2, 3, 1, 2, 3) (1, 2, 3)
可变对象列表
# 列表
a = [1, 2, 3]
b = a
print(id(a))
print(id(b))
print(a, b)
a += [4, 5, 6]
print(a, b)
print(id(a))
print(id(b))
# 输出结果
4327665856
4327665856
[1, 2, 3, 4, 5, 6] [1, 2, 3, 4, 5, 6]
4327665856
4327665856
能看到 a 变量修改值之后,b 的值也随之修改了
可以看看下面的图
因为 list 是不可变对象,所以并不会将原来的值复制到新的内存地址再改变,而是直接在原来的内存地址上修改数据
因为 a、b 都是指向原来的内存地址的,所以 a、b 变量保存的内存地址是一致的(对象引用是一致的),当然值也是一样的啦
Python 函数的参数传递
这里先提前讲下函数的入门,因为参数传递是个挺重要的点
概念
开头有讲到,Python 的一切传递都是对象的引用,函数参数传递也不例外
当传递给函数的是一个变量,实际上传递的是变量保存的对象引用(变量指向的内存地址)
在函数内部修改变量时,会根据变量指向的内存地址,去修改对应的值才对,事实真是如此吗
参数传递不可变对象
# 函数
def test_no_define(age, name):
age = 123
name = "poloyy"
print(age, name)
age = 1
name = "yy"
print(age, name)
test_no_define(age, name)
print(age, name)
# 输出结果
1 yy
123 poloyy
1 yy
参数传递可变对象
# 函数
def test_define(dicts, sets):
dicts['age'] = 24
sets.pop()
print(dicts, sets)
dicts = {"age": 123}
sets = {1, 2}
print(dicts, sets)
test_define(dicts, sets)
print(dicts, sets)
# 输出结果
1 yy
{'age': 123} {1, 2}
{'age': 24} {2}
{'age': 24} {2}
总结
当函数参数传递的变量是不可变对象的时候,函数内改变变量值,函数外的变量不会随之改变
当函数参数传递的变量是可变对象的时候,函数内改变变量值,函数外的变量会随之改变
来源:https://www.cnblogs.com/poloyy/p/15073168.html


猜你喜欢
- 本文实例为大家分享了js动态时间显示 的具体代码,供大家参考,具体内容如下<!doctype html><html>
- 效果图:搜索分类2种情况,一般的是当用户输入完,点击确定的按钮在向后发送请求,还有一种就是的我一边输入,一边向后台发送请求,但是会产生一个性
- 一、软件下载与安装VScode下载地址:https://code.visualstudio.com/VScode的github项目地址(本文
- 1、Mongoose模块(1)是一个对象模型工具,是对Node.js环境下操作MongoDB数据库进行了封装,可以将MongoDB数据库中的
- 目录技术背景加速场景基于Numba的GPU加速总结概要技术背景GPU加速是现代工业各种场景中非常常用的一种技术,这得益于GPU计算的高度并行
- 获取数据(四种方式)1. url: 需要正则去匹配 url(r'^index/(num)/$
- numpy下fft模块提供了丰富的fft函数,几种常用的在这里记录一下使用方式fft输入实数samples,如果输入的sample是带虚数部
- 1.彻底弄懂CSS盒子模式一(DIV布局快速入门) 2.彻底弄懂CSS盒子模式二(导航栏实例) 4.彻底弄懂CSS盒子模式四(绝对定位和相对
- PyAutoGUI是一个Python语言的键鼠自动化库,简单来说和按键精灵的功能一样。但是因为是Python的类库,所以可以使用Python
- 一、模拟登录图书馆管理系统我们可以先看一下登录页面(很多学校这些管理系统页面就是很low):两种方式去模拟登录图书馆:1. 构造登录表单进行
- Spark Streaming VS Structured StreamingSpark Streaming是Spark最初的流处理框架,使
- 不管是写自定义标签还是过滤器,第一件要做的事是创建模板库(Django能够导入的基本结构)。创建一个模板库分两步走:
- PC端项目中经常会出现大量的数据列表页面,涉及到下拉框选择筛选条件;当时用到bootstrap-select下拉框时该如何点击重置按钮就清除
- 理解一个算法最快,最深刻的做法,我觉着可能是自己手动实现,虽然项目中不用自己实现,有已经封装好的算法库,供我们调用,我觉着还是有必要自己亲自
- 在日常开发中,经常遇到针对字符串的替换、截取,知识点比较碎容易混淆,特此总结一下,仅供参考。一、替换第一个匹配项字符串替换let strte
- 目录用Python写一个简单的通讯录一、构思1、定义空列表和一个空字典来存储2、定义功能选项3、添加通讯录功能4、 循环,调用所有的函数功能
- 很多人在使用AJAX调用别人站点内容的时候,JS会提示"没有权限"错误,这是XMLHTTP组件的限制-安全起见禁止访问非
- 背景我们先来看看MySQL 8.0的事务提交的大致流程以上流程,是MySQL8.0对WAL原则的一种实现,这个流程意味着,任何一个事务的提交
- 本文实例讲述了php版银联支付接口开发的方法。分享给大家供大家参考,具体如下:支付接口现在有第三方的支付接口也有银行的支付接口。这里就来介绍
- 前言当前二维码的应用越来越广泛,包括疫情时期的健康码也是应用二维码的典型案例,最近需要通过一张二维码显示较多文本数据,也就是对二维码数据进行