可变对象和不可变对象

在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中的可变、不可变对象和赋值技巧序列解包的更多相关文章

  1. Python中为什么不能用可变对象作为默认参数的值

    def func(numbers = [], num=1): numbers.append(num) for number in numbers: print(number) func() >& ...

  2. Python中使用operator模块实现对象的多级排序

    Python中使用operator模块实现对象的多级排序 今天碰到一个小的排序问题,需要按嵌套对象的多个属性来排序,于是发现了Python里的operator模块和sorted函数组合可以实现这个功能 ...

  3. 在python中元组与列表的区别及序列解包

    一. 元组与列表的区别 元组中的数据一旦定义就不允许更改. 元组没有append().extend()和insert()等方法,无法向元组中添加元素. 元组没有remove()或pop()方法,也无法 ...

  4. Python中序列解包与函数的参数收集之间的关系

    在<第4.7节 Python特色的序列解包.链式赋值.链式比较>中老猿介绍了序列解包,<第5.2节 Python中带星号的函数参数实现参数收集>介绍了函数的参数收集,实际上函数 ...

  5. Python特色的序列解包、链式赋值、链式比较

    一.序列解包 序列解包(或可迭代对象解包):解包就是从序列中取出其中的元素的过程,将一个序列(或任何可迭代对象)解包,并将得到的值存储到一系列变量中. 一般情况下要解包的序列包含的元素个数必须与你在等 ...

  6. 第4.7节 Python特色的序列解包、链式赋值、链式比较

    一.序列解包 序列解包(或可迭代对象解包):解包就是从序列中取出其中的元素的过程,将一个序列(或任何可迭代对象)解包,并将得到的值存储到一系列变量中. 一般情况下要解包的序列包含的元素个数必须与你在等 ...

  7. python 零散记录(五) import的几种方式 序列解包 条件和循环 强调getattr内建函数

    用import关键字导入模块的几种方式: #python是自解释的,不必多说,代码本身就是人可读的 import xxx from xxx import xxx from xxx import xx1 ...

  8. Python——序列封包与序列解包

    一.序列封包与序列解包 把多个值赋给一个变量时,Python会自动的把多个值封装成元组,称为序列封包. 把一个序列(列表.元组.字符串等)直接赋给多个变量,此时会把序列中的各个元素依次赋值给每个变量, ...

  9. python 序列解包(解压缩)

    序列解包(解压缩) 所学的解压缩 如果我们给出一个列表,我们需要一次性取出多个值,我们是不是可以用下面的方式实现呢? name_list = ['nick', 'egon', 'jason'] x = ...

随机推荐

  1. CentOS 7.6下解决登录MySQL时,ERROR 1045 (28000): Access denied for user root@localhost (using password: YES

    https://blog.csdn.net/sinat_35406909/article/details/79763782

  2. git权限

    itHub--创建新的分支(转) 如何在 GitHub 的项目中创建一个分支呢? 其实很简单啦,直接点击 Branch,然后在弹出的文本框中添加自己的 Branch Name 然后点击蓝色的Creat ...

  3. Python h5py

    1.关于安装: 如果你使用的是Anaconda的话,安装命令如下: conda install h5py 如果没有,安装命令如下: pip install h5py 2.核心概念 读取HDF5文件,假 ...

  4. Notes : <Hands-on ML with Sklearn & TF> Chapter 4

    .caret, .dropup > .btn > .caret { border-top-color: #000 !important; } .label { border: 1px so ...

  5. webveiw返回死循环问题以及在打开web页面会唤起浏览器打开的问题解决

    微信.QQ空间等大量软件都内嵌了H5,不得不说这是一种趋势,Andriod与H5互调可以让我们实现混合开发.至于混合开发就是在一个App中内嵌一个轻量级的浏览器,一部分原生的功能改为Html 5来开发 ...

  6. 20155205 郝博雅 《网络对抗技术》Exp1 PC平台逆向破解

    20155205 郝博雅 <网络对抗技术>Exp1 PC平台逆向破解 一.实验准备 1. 掌握NOP.JNE.JE.JMP.CMP汇编指令的机器码 NOP:NOP指令即"空指令& ...

  7. js操作bom和dom

    Bom 概念 BOM : Browser Object Model 浏览器对象模型,描述与浏览器进行交互的方法和接 口, ECMAscript是javascript的核心,但如果要在web中使用jav ...

  8. day_8文件的操作

    首先昨天讲的内容有 类型转换 1:数字类型: int ()  bool()  float() 2:str 与int:    int('10') | int('-10') | int('0') | fl ...

  9. Centos6.5---samba文件共享服务配置(二)

    Linux-----samba服务配置(二) 需求: 某公司销售部门提出一个文件共享需求,要求部门共享目录有三个,第一个共享目录所有销售部门人员都具有可读可写权限:第二个共享目录所有销售人员只读权限, ...

  10. PHP的生命周期: