python基础:不可变对象与可变对象

不可变对象与可变对象分类

什么是可变对象和不可变对象

python中,一切皆对象,对象有3个重要的因素:变量、取值、id。

比如赋值语句:a=123,变量a指向123,123是取值,id是123这个数值在内存中的地址。

对于列表,表示如下:

 

可变对象和不可变对象的区别在于是否可以“原地”修改变量的取值。不可变对象,不能“原地”修改变量的取值。可变对象,可“原地”修改变量的取值。

不可变对象不会“原地”修改变量的取值

不可变对象包括:数字、字符串、元组。是客观存在的,当试图通过赋值等操作,修改变量的取值时,并没有真正的改变变量的取值,而是将变量指向了新的取值。

关于不可变对象,最经常的理解是:不可变对象,不会重新分配新的内存空间。这种说法,其实并不完全对。

  1. 没有重新分配新的内存空间
>>> a = 123
>>> b = 123
>>> print(id(a), id(b))
4537342432 4537342432
>>> 
>>> astr = "Hello"
>>> bstr = "Hello"
>>> print(id(astr), id(bstr))
4541756080 4541756080

如上,数字a和b,所指向的内存空间一样;字符串astr和bstr指向的内存空间也一样,并没有重新在分配新的内存空间。

    2. 重新分配内存空间,python的intern机制

然而,并不是所有的不可变对象,都不分配内存空间。下面的例子中,可以看到浮点数和字符串,内存地址并不一致,有重新分配内存空间。

>>> c = 1.1
>>> d = 1.1
>>> print(id(c), id(d))
4541726160 4541726128
>>> 
>>> cstr = "hello world"
>>> dstr = "hello world"
>>> print(id(cstr), id(dstr))
4541756336 4541756144

这就是python的intern机制:如果缓存中有内存地址,直接取缓存中的内存地址;如果没有,重新分配新的内存地址。

不可变对象-数:只有整数,且范围在[-5, 256]这一范围内,不用重新分配内存空间,直接取缓存中内存空间即可,包括bool类型的True和False,直接取缓存内存地址;对于浮点数,重新分配新的内存空间。

不变变对象-字符串:只有对类似标识符的字符串才有缓存,不用重新分配内存空间;其他字符串需重新分配内存空间。

不可变对象-元组:重新分配内存空间,用数组来实现。

无论是否重新分配内存空间,不可变对象都是客观存在,对不可变对象的变量操作,并没有改变不可变对象的取值,而是变量重新指向了新的内存空间(包括缓存和新开辟的内存)。

# astr指向了新的内存空间,并没有直接在“Hello”所在内存空间内改变
>>> astr = "Hello"
>>> print(id(astr))
4541757168
>>> astr = astr + " world"
>>> print(id(astr))
4541756336

 

任何试图“原地”修改不可变对象的操作,将不可行。

>>> astr = "Hello"
>>> print(id(astr))
4541756080
>>> astr[0] = "T"
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'str' object does not support item assignment


>>> atuple = ("a", "b", "c")
>>> atuple[0] = "M"
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment

 

  • 可变对象,"原地"修改

  1. 可变对象,都是重新分配新的内存空间。
>>> clist = ["a", "b", "c"]
>>> dlist = ["a", "b", "c"]
>>> print(id(clist), id(dlist))
4541747008 4541755456

    2. 对不可变对象的操作,都是"原地"进行

>>> clist = ["a", "b", "c"]
>>> print(id(clist))
4541747008
>>> clist.append("m")
>>> print(id(clist))
4541747008

 

赋值

可变对象和不可变对象,都是将目前所指的内存地址复制给变量,也就是说,该取值会新增一个引用对象。不同在于,不可变对象,其中的一个变量修改了,会指向新的内存空间,而可变对象是“原地”修改,任一变量的修改,都会影响到对象的修改。

不可变对象赋值:a = 123, b = a,此时变量a和b指向新的相同的内存空间:

可变对象赋值:alist = ["a", "b", "c"],blist = alist,此时变量alist和blist指向的也是相同的内存空间:

但是,当修改不可变对象的其中取值时,变量会指向新的内存空间,并不会修改原来的取值。

可变对象,修改任一一个变量,都会影响到原来的取值,因为都是“原地”修改,如下修改alist或blist任一,都会更改列表。

 

is和==,type

is需是内存地址和取值都相同

>>> a = 257
>>> b = 257
>>> a is b
False
>>> a == b
True

上面例子中,a=257,不在[-5, 256]这一整数范围内,需要重新分配新的内存空间,所以a is b得到的结果是False

==是取值相同

 

列表的赋值

  • lst_copy = lst,列表lst和lst_copy 是同一个id地址
  • lst_copy = lst[:],列表lst和lst_copy不是同一个id地址,新开辟了新的内存空间。

浅拷贝

对不可变对象:不分配内存空间

对可变对象:只分配父类内存空间,没有分配其子类的内存空间。

import copy

alist = ["a", "b", [1, 2, 3]]
blist = copy.copy(alist)
alist[2].append(4)
print(id(alist), id(blist))
print(id(alist[2]))
print(id(blist[2]))

调用copy.copy(x)方法,虽然alist和blist有不同的内存地址,但是其元素[1, 2, 3]并没有重新分配新的内存空间。可以图解如下:

 

深拷贝

复制内存空间,包括复制了父类的内存空间及其子类的内存空间。深度拷贝后,完全是2个独立的对象,不会互相影响。

import copy

alist = ["a", "b", [1, 2, 3]]
blist = copy.deepcopy(alist)
alist[2].append(4)
print(id(alist), id(blist))
print(alist)
print(blist)

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值