前置知识

  • 在 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}

总结

  • 当函数参数传递的变量是不可变对象的时候,函数内改变变量值,函数外的变量不会随之改变
  • 当函数参数传递的变量是可变对象的时候,函数内改变变量值,函数外的变量会随之改变

Python - 可变和不可变对象的更多相关文章

  1. python引用在函数传参时的体现以及可变与不可变对象的对比

    今天偶然看到 vamei 老师的博客 http://www.cnblogs.com/vamei/archive/2012/07/10/2582795.html 讲的是python动态类型以及引用的事儿 ...

  2. 关于python的可变和不可变对象

    在python中所有都是对象,在python中只有list和dict是可变对象,其他都是不可变对象. 具体参照:http://www.cnblogs.com/lovemo1314/archive/20 ...

  3. python 基础使用list、dict、set、可变与不可变对象

    参考链接:https://www.liaoxuefeng.com/wiki/1016959663602400/1017104324028448 dict是字典,可以储存键值对类型的值,set与dict ...

  4. Python中的不可变对象类型与可变对象类型

    https://blog.csdn.net/answer3lin/article/details/86430074 其实各个标准资料中没有说明Python有值类型和引用类型的分类,这个分类一般是C++ ...

  5. python基础之pip、.pyc、三元运算、进制、一切皆对象、可变与不可变类型

    一.pip(下载工具==yum) 1.重点(必须掌握的) 列出已安装的包 pip list 安装要安装的包 pip install xxx 安装特定版本 pip install django==1.1 ...

  6. python 可变数据类型&不可变数据类型

    在python中,数据类型分为可变数据类型和不可变数据类型,不可变数据类型包括string,int,float,tuple,可变数据类型包括list,dict. 所谓的可变与不可变,举例如下: > ...

  7. Python数据类型的可变与不可变

    首先,我们需要知道在python中哪些是可变数据类型,哪些是不可变数据类型.可变数据类型:列表list和字典dict:不可变数据类型:整型int.浮点型float.字符串型string和元组tuple ...

  8. python的可变与不可变数据类型

    <python的可变与不可变数据类型>     python与C/C++不一样,它的变量使用有自己的特点,当初学python的时候,一定要记住“一切皆为对象,一切皆为对象的引用”这句话,其 ...

  9. Python3_函数参数传递、可变与不可变对象、变量作用域、函数返回值

    参数传递: 在 python 中,类型属于对象,变量是没有类型的:(变量都仅仅是一个对象的引用,没有类型之分)a=[1,2,3] a="Runoob" 以上代码中,[1,2,3] ...

随机推荐

  1. 基于Android平台的图书管理系统的制作(2)

    上一篇讲解了制作图书管理系统的初衷与要求,和app首页的代码. 下面来介绍图书管理系统的服务对象:学生 学生类的设计: 个人信息:账号.密码.姓名.学号.邮箱.年龄. 借阅信息:借阅总数(不超过十本) ...

  2. Optim High Performance Unload [HPU]

    目录 一.简介 二.安装 三.基本配置 四.卸数 4.1. 命令行导出 4.2. 控制文件 4.2.1. 从表空间容器卸数 4.2.2. 从备份镜像中卸数 4.2.3. 控制文件语法参考 4.3. 存 ...

  3. 三、WPF入门教程——布局和常用Panel学习

    布局和常用Panel学习 一.简介 所有的WPF布局容器都派生自System.Windows.Controls.Panel.Panel继承自FrameworkElement. 在Panel中有一个比较 ...

  4. Oracle数据库delete删除普通堆表千万条记录

    Oracle数据库delete删除普通堆表千万条历史记录. 直接删除的影响: 1.可能由于undo表空间不足从而导致最终删除失败的问题: 2.可能导致undo表空间过度使用,影响到其他用户正常操作. ...

  5. SpringBoot_登录注册

    学习SpringBoot需要的前期基础 Spring(Bean容器 IOC set 构造方法 AOP) SpringMVC(GET POST Restful) 对于SpringBoot,约定大于配置 ...

  6. Golang编写动态库实现回调函数

    Golang编写动态库实现回调函数 我们现在要做一个动态库,但是C++实在是比较难,于是就想能不能用更简单的golang来实现,golang也就是最近的版本才支持编译成动态库,在网上也没找到可用的案例 ...

  7. Redis高并发快的3大原因详解

    1. Redis的高并发和快速的原因 1.redis是基于内存的,内存的读写速度非常快: 2.redis是单线程的,省去了很多上下文切换线程的时间: 3.redis使用多路复用技术,可以处理并发的连接 ...

  8. Gym 100008E Harmonious Matrices 高斯消元

    POJ 1222 高斯消元更稳 看这个就懂了 #include <bits/stdc++.h> using namespace std; const int maxn = 2000; in ...

  9. 企业该选择什么样的CRM系统

    不论您是需要CRM系统来优化业务流程,还是准备更换一款新的CRM系统,在这之前都应该先明确企业的需求,并了解CRM的哪些功能能够对企业有所帮助.例如,企业的管理者想了解每个销售人员的业绩情况,那么就应 ...

  10. Linux:Ubuntu配置jdk环境变量

    1.下载jdk 推荐官网:https://www.oracle.com/java/technologies/javase/javase-jdk8-downloads.html 下载tar.gz包即可. ...