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 = ...
随机推荐
- DOTween的基本用法
首先声明一点,不要简单的认为 DOTween 只能用在 Transform 组件上完成一些简单的动画,或者是完成一些 UI 动画,DOTween 的用途是很广的,unity中有很多组件都可以使用 DO ...
- PLSQL设置细节
1. tnsnames.ora 文件设置中,前面不能包含空格,否则:无法解析连接字符串 2. 当一切配置都正确,但是还是无法连接:“身份证明检索失败” 解决:打开tns_admin配置连接串的目录,修 ...
- REdis之RDB配置问题
RDB配置:save 900 1save 300 10save 60 10000stop-writes-on-bgsave-error nordbcompression yesrdbchecksum ...
- vuejs 使用vue-cli引入bootstrap
前言:对于刚刚进入vuejs的队伍中的小白来讲,很多都是模糊的,js操作dom节点的思想萦绕,还不能自由切换在二者之间. 解决之道: 想要在vue中引入bootstrap,引入的时候需要按照如下的步骤 ...
- pycharm License server激活
2018-11-15 pycharm License server激活有效:https://idea.ouyanglol.com/
- Linux-3.0.8 input subsystem代码阅读笔记
先乱序记录一下阅读Linux input subsystem代码的笔记. 在input device driver的入口代码部分,需要分配并初始化input device结构,内核提供的API是inp ...
- Memcached未授权访问
概念 memcached是一个内存中的键值存储区,用于存储来自数据库调用.API调用或页面呈现结果的任意小数据块(字符串.对象).memcached简单但功能强大.其简单的设计促进了快速部署.易于开发 ...
- Hashtable与Dictionary比较
项目需要存储Tcp连接对象,考虑使用Hashtable或者Dictionary存储.Hashtable在查询方面有优势,Dictionary在确定类型下不需要拆箱与装箱有优势.于是,写了个demo对两 ...
- windos64位下python3.6安装pywin32的问题
~~~~今天终于算是正式接触scrapy了,测试的时候发现少装了一个pywin32的模块,然后安装了好久,中间碰到好多坑,最后总算是装好了. 首先我自己的py3.6是64位版本的,这是pywin32模 ...
- 在ASP.NET Core MVC中构建简单 Web Api
Getting Started 在 ASP.NET Core MVC 框架中,ASP.NET 团队为我们提供了一整套的用于构建一个 Web 中的各种部分所需的套件,那么有些时候我们只需要做一个简单的 ...