Python中的可变对象和不可变对象

一、python中的变量与对象

首先在理解python的可变对象和不可变对象时,要理解python的赋值操作。在python中,一切事物皆是对象,变量是对象在内存中的存储和地址的抽象


对变量的理解

在Python中,类型是属于对象的,而不是变量, 变量和对象是分离的,对象是内存中储存数据的实体,变量则是指向对象的指针。

“=”(赋值号)是将右侧对象的内存地址赋值给左侧的变量。
当我们写下面语句时

a = "abc"

Python解释器其实顺序干了两件事情:
1 在内存中创建一个字符串“abc”;
2 在内存中创建一个名为“a”的变量,并将“a”指向字符串“abc”(将“abc”的地址保存到“a”中)。
这样我们就能通过操作“a”而改变内存中的“abc”。

二、什么是可变对象/不可变对象

  • 不可变对象 该对象所指向的内存中的值不能被改变当改变一个变量的时候,由于改变量所指向的内存中的值不能被改变,因此会将原来的值复制一份后再改变,具体做法是重新开辟一块空间存放新的值,并将该变量指向这个空间。原来空间中的值如果没有变量指向它,会被当做垃圾回收。数值类型(int float), 字符串str, 元组tuple是不可变的数据类型
  • 可变对象 该对象所指向的内存中的值可以被改变。变量(准确的说是引用)改变后,实际上是其所指的值直接发生改变,并没有发生复制行为,也没有开辟新的出地址,通俗点说就是原地改变。列表list, 字典dist, 集合set是可变的数据类型

举例说明

  • 不可变对象的例子
>>> a = 'hello'
>>> id(a)
1921615688624
>>> a = 'world'
>>> id(a)
1921615689744
# 重新赋值之后,变量a的内存地址已经变了
# 'hello'是str类型,不可变,所以赋值操作重新创建了str对象 'world'对象,
# 然后将变量a指向了它
  • 可变对象的例子
>>> lst = [1, 2, 3]
>>> id(lst)
1921615726216
>>> lst.append(4)
>>> id(lst)
1921615726216
# list重新赋值之后,变量lst的内存地址并未改变
# [1, 2, 3]是可变的,append操作只是改变了其value,变量lst指向没有变

三、python引用

python一般内部赋值变量的时候,都是传递一个引用变量,和C语言的传地址的概念差不多, 比如:

>>> a = [1,2,3] # 表示变量a保存了这个列表的地址
# python里可以用id()来查询下a在内存的地址是:1921615756616
>>> id(a)
1921615756616
>>> b = a
# 那b的内容是什么,地址又是什么呢?
# 用print 输出下b的内容也是[1,2,3]
# 然后我们查看下b的地址看下能否验证我们的结论
>>> b
[1, 2, 3]
# 果然b的地址也是:1921615756616
>>> id(b)
1921615756616

这样会带来一个问题,因为变量a,和变量b都是保存了同一个列表的地址。如果我改变a指向的列表的值的话,那b指向的列表的值也同时改变

>>> a[1] = 6
>>> a
[1, 6, 3]
>>> b
[1, 6, 3]

如果我们只想修改a列表里面的内容。而不想修改b的内容,那就要用到python的拷贝了

>>> a = [1,2,3]
>>> b = a[:]
>>> a[1] = 6
>>> a
[1, 6, 3]
>>> b
[1, 2, 3]

四、函数值传递

首先来看一个例子

def func_int(a):
    a += 4
 
def func_list(lst):
    lst[0] = 4
    
t = 0
func_int(t)
print t
# output: 0
 
t_list = [1, 2, 3]
func_list(t_list)
print t_list
# output: [4, 2, 3]

主要是因为可变对象和不可变对象的原因:对于可变对象,对象的操作不会重建对象,而对于不可变对象,每一次操作就重建新的对象。

在函数参数传递的时候,Python其实就是把参数里传入的变量对应的对象的引用依次赋值给对应的函数内部变量。参照上面的例子来说明更容易理解,func_int中的局部变量"a"其实是全部变量"t"所指向对象的另一个引用,由于整数对象是不可变的,所以当func_int对变量"a"进行修改的时候,实际上是将局部变量"a"指向到了整数对象"1"。所以很明显,func_list修改的是一个可变的对象,局部变量"a"和全局变量"t_list"指向的还是同一个对象。

五、深拷贝 & 浅拷贝

接下来的问题是:如果我们一定要复制一个可变对象的副本怎么办?简单的赋值已经证明是不可行的,所以Python提供了copy模块,专门用于复制可变对象。
copy中有两个方法:copy()和deepcopy(),前一个是浅拷贝,后一个是深拷贝
浅拷贝仅仅复制了第一个传给它的对象,下面的不管了;而深拷贝则将所有能复制的对象都复制了。

>>> import copy
>>> a = [[1,2,3], [4,5,6]]
>>> b = a
>>> c = copy.copy(a)
>>> d = copy.deepcopy(a)
>>>
>>> a.append(15)
>>> a[1][2] = 10
>>> a
[[1, 2, 3], [4, 5, 10], 15]
>>> b
[[1, 2, 3], [4, 5, 10], 15]
>>> c
[[1, 2, 3], [4, 5, 10]]
>>> d
[[1, 2, 3], [4, 5, 6]]

六、作用域

在Python程序中创建、改变或查找变量名时,都是在一个保存变量名的地方进行中,那个地方我们称之为命名空间。作用域这个术语也称之为命名空间。

变量名引用分为三个作用域进行查找:首先是本地,然后是函数内(如果有的话),之后是全局,最后是内置。在默认情况下,变量名赋值会创建或者改变本地变量。全局声明将会给映射到模块文件内部的作用域的变量名赋值。Python 的变量名解析机制也称为 LEGB 法则,具体如下:

当在函数中使用未确定的变量名时,Python搜索4个作用域:

  • 本地作用域(L)
  • 上一层嵌套结构中 def 或 lambda 的本地作用域(E)
  • 全局作用域(G)
  • 内置作用域(B)。

按这个查找原则,在第一处找到的地方停止。如果没有找到,Python 会报错的。

引用来源
1 Python中的可变对象和不可变对象
2 Python值传递还是引用传递
3 Python中的变量、引用、拷贝和作用域

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,524评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,869评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,813评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,210评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,085评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,117评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,533评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,219评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,487评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,582评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,362评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,218评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,589评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,899评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,176评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,503评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,707评论 2 335

推荐阅读更多精彩内容