Python中的可变、不可变对象和赋值技巧序列解包
可变对象和不可变对象
在python中一切皆对象。在Python中不存在所谓的值传递调用,一切传递都是对象的引用,也可认为是传址。
python中,对象分为可变(mutable)和不可变(immutable)两种类型,元组(tuple)、数值型(number)、字符串(string)均为不可变对象,而字典型(dictionary)和列表型(list)的对象是可变对象。
不可变对象
见一个例子,分析不可变对象的特点
python内置id()函数,用于返回对象的唯一标识(identity)。id()返回的是十进制,对象实际内存地址为hex(id(object)),本文中将id()与hex(id())等价使用。
>>> a = 1 #将变量a与内存中的值为1的内存绑定在一起
>>> a = 2 # 将变量a与内存中值为2的内存绑定在一起,并不是修改原来a绑定的内存中的值,
# 这时,原来的这个值为1的内存地址的引用次数减一,当引用计数为0时,内存地址被回收
>>> b = a # 变量b绑定与a一样的内存
>>> id(b),id(a) # 打印a,b的绑定的内存地址
(1972461824, 1972461824)
>>> b = 3 # 创建一个内存值为3的内存地址与变量名字b进行绑定。这时,a还是指向值为2的内存地址
>>> a,b
(2, 3)
>>> id(b),id(a) # 打印a,b的绑定的内存地址
(1972461856, 1972461824)
>>> x = 1
>>> y = 1
>>> z = 1
>>> x is y
True
>>> y is z
True
>>> id(x),id(y),id(z)
(1972461792, 1972461792, 1972461792)
从第二个例子可看出因为整数为不可变对象,x,y,z在内存中均指向一个值为1的内存地址。
不可变对象最大的优点便是减少重复的值对内存空间的占用。
缺点便是如第一个例子中所示,我要修改这个变量绑定的值,如果内存中没有存在该值的内存块,那么必须重新开辟一块内存,把新地址与变量名绑定。
而不是修改变量原来指向的内存块的值,这回给执行效率带来一定的降低。
原来的内存块会因变量与其他内存块绑定而引用次数减1.
下面是对第一个例子的图解

可变对象
继续看一个例子
例1
>>> a = [1]
>>> b = a # a,b绑定同一内存地址,内存中值为[1]
>>> id(a),id(b)
(1991064334856, 1991064334856)
>>> b.append(2)
>>> a,b
([1, 2], [1, 2])
变量名a和b是绑定的同一内存地址,对任一个变量对应的值的修改,都会反映到另一个变量上。也就是说对可变对象的操作,是直接对这个对象进行改变。

例2
>>> a =[1]
>>> b=[1]
>>> id(a),id(b)
(1991065258248, 1991064760520)
由此可见,在可变对象中值相同的变量绑定的不一定是相同的内存地址,会指向不同的对象。

例3
在函数默认形参值中使用可变对象会出现一个大坑见例子:
>>> def add_end(L=[]):
... L.append('End')
... return L
...
>>> add_end([1,2])
[1, 2, 'End']
>>> add_end([a,b])
[[1], [1], 'End']
>>> add_end()
['End']
>>> add_end()
['End', 'End']
>>> add_end()
['End', 'End', 'End']
这里当正常传参调用函数时一切正常,可是不断调用带默认形参是值得函数时,我们觉得的答案应该是['End'],但是仿佛次调用都记住了上次的值。
这原因为何?
这看起来有点像是每次调用的默认形参值变化了。我们用add_end.__defaults__来查看函数对象的默认参数变化情况
>>> def add_end(L=[]):
... L.append('End')
... return L
...
>>> add_end.__defaults__
([],)
>>> add_end()
['End']
>>> add_end.__defaults__
(['End'],)
>>> add_end()
['End', 'End']
>>> add_end()
['End', 'End', 'End']
>>> add_end.__defaults__
(['End', 'End', 'End'],)
由上可知每调用一次带默认形参值的函数,该函数对象的默认形参值就会发生变化,所以下次调用的默认形参值就是变化过后的。究其原因还是因为默认形参值为可变对象,导致每次调用会改变默认形参值。
所以,在编程时可尽量将对象设计为不变对象,可以避免一些麻烦。
赋值操作技巧---序列解包(递归解包)
将含多个值的序列解开,放到变量的序列中。解包序列中元素的数量必须和赋值符号=左边变量数量一致,否则会报错,参数太多或太少。
>>> values = 1,2,3
>>> values
(1, 2, 3)
>>> x,y,z = values
>>> print(x,y,z)
1 2 3
>>> a,b,c = 1,2,3,4,5
Traceback (most recent call last):
File "<input>", line 1, in <module>
ValueError: too many values to unpack (expected 3)
>>> a,b,c=1,2
Traceback (most recent call last):
File "<input>", line 1, in <module>
ValueError: not enough values to unpack (expected 3, got 2)
当解包序列中的元素多多余变量数量时,变量序列中可以使用星号运算符*,对值进行收集
>>> a,b,*c=1,2,3,4,5
>>> print(a,b,c)
1 2 [3, 4, 5]
Python中的可变、不可变对象和赋值技巧序列解包的更多相关文章
- Python中为什么不能用可变对象作为默认参数的值
def func(numbers = [], num=1): numbers.append(num) for number in numbers: print(number) func() >& ...
- Python中使用operator模块实现对象的多级排序
Python中使用operator模块实现对象的多级排序 今天碰到一个小的排序问题,需要按嵌套对象的多个属性来排序,于是发现了Python里的operator模块和sorted函数组合可以实现这个功能 ...
- 在python中元组与列表的区别及序列解包
一. 元组与列表的区别 元组中的数据一旦定义就不允许更改. 元组没有append().extend()和insert()等方法,无法向元组中添加元素. 元组没有remove()或pop()方法,也无法 ...
- Python中序列解包与函数的参数收集之间的关系
在<第4.7节 Python特色的序列解包.链式赋值.链式比较>中老猿介绍了序列解包,<第5.2节 Python中带星号的函数参数实现参数收集>介绍了函数的参数收集,实际上函数 ...
- Python特色的序列解包、链式赋值、链式比较
一.序列解包 序列解包(或可迭代对象解包):解包就是从序列中取出其中的元素的过程,将一个序列(或任何可迭代对象)解包,并将得到的值存储到一系列变量中. 一般情况下要解包的序列包含的元素个数必须与你在等 ...
- 第4.7节 Python特色的序列解包、链式赋值、链式比较
一.序列解包 序列解包(或可迭代对象解包):解包就是从序列中取出其中的元素的过程,将一个序列(或任何可迭代对象)解包,并将得到的值存储到一系列变量中. 一般情况下要解包的序列包含的元素个数必须与你在等 ...
- python 零散记录(五) import的几种方式 序列解包 条件和循环 强调getattr内建函数
用import关键字导入模块的几种方式: #python是自解释的,不必多说,代码本身就是人可读的 import xxx from xxx import xxx from xxx import xx1 ...
- Python——序列封包与序列解包
一.序列封包与序列解包 把多个值赋给一个变量时,Python会自动的把多个值封装成元组,称为序列封包. 把一个序列(列表.元组.字符串等)直接赋给多个变量,此时会把序列中的各个元素依次赋值给每个变量, ...
- python 序列解包(解压缩)
序列解包(解压缩) 所学的解压缩 如果我们给出一个列表,我们需要一次性取出多个值,我们是不是可以用下面的方式实现呢? name_list = ['nick', 'egon', 'jason'] x = ...
随机推荐
- Django积木块三——静态文件和上传文件
静态文件和上传的文件 # 静态文件 STATIC_URL = '/static/' STATICFILES_DIRS = ( os.path.join(BASE_DIR, 'static'), ) # ...
- python 单例模式的四种实现方法及注意事项
一.模块单例 Python 的模块就是天然的单例模式,因为模块在第一次导入时,会生成 .pyc 文件,当第二次导入时,就会直接加载 .pyc 文件,而不会再次执行模块代码. #foo1.py clas ...
- [转] KVM storage performance and cache settings on Red Hat Enterprise Linux 6.2
Almost one year ago, I checked how different cache settings affected KVM storage subsystem performan ...
- 在ASP.NET MVC里对Web Page网页进行权限控制
我们在ASP.NET MVC开发时,有时候还是得设计ASP.NET的Web Page网页(.aspx和.aspx.cs),来实现一些ASP.NET MVC无法实现的功能,如此篇<Visual S ...
- C#通过COM组件操作IE浏览器(二):使用IHTMLDocument3完成登录
第一章介绍了如何打开网站,这一章介绍一下使用IHTMLDocument3完成登录博客园,以下为代码: SHDocVw.InternetExplorer oBrowser = new SHDocVw.I ...
- 脑残式网络编程入门(五):每天都在用的Ping命令,它到底是什么?
本文引用了公众号纯洁的微笑作者奎哥的技术文章,感谢原作者的分享. 1.前言 老于网络编程熟手来说,在测试和部署网络通信应用(比如IM聊天.实时音视频等)时,如果发现网络连接超时,第一时间想到的就是 ...
- 致我们再也回不去的 Github ...
本文原文来自公众号 stormzhang,原作者自称“二流学校毕业.非科班出身.从 0 自学编程到现在的「段子张」”. 1.前言 相信大家都知道了,微软已确认要收购 GitHub 了,Github ...
- 还在使用SimpleDateFormat?
阅读本文大概需要 3.2 分钟. 前言 日常开发中,我们经常需要使用时间相关类,想必大家对SimpleDateFormat并不陌生.主要是用它进行时间的格式化输出和解析,挺方便快捷的,但是Simple ...
- Python面向对象2:类与对象的成员分析及self
# 3. 类和对象的成员分析- 类和对象都可以存储成员,成员可以归类所有,也可以归对象所有- 类存储成员时使用的是与类关联的一个对象- 独享存储成员是是存储在当前对象中- 对象访问一个成员时,如果对象 ...
- 细说MySQL数据库操作
目录 基本语法: 字符集和校验规则 字符集 校验规则 校验规则的影响 数据库操作相关指令 查询数据库版本 显示数据库语句 显示数据库创建语句 数据库删除语句 查看当前数据库有多少个用户在操作 基本语法 ...