导航:首页 > 编程语言 > 不可变对象python

不可变对象python

发布时间:2023-03-31 02:33:42

‘壹’ 谁能给简单通俗点解释下python里可变对象、不变对象、变量三者的关系

①python中,万物皆对象。
②python中,没有绝对常量,有的是对常量的引用。
③python中,可变对象,表示的是对象内容可以改变,比如list ,dist
④python中,不可变对象,表示的是对象内容不可以改变,如字符串,tuple等
变量和对象,这两个很难解释,我个人理解是,比如在python中 i = 1,i是一个变量,也同时是一个对象,在python中, 变量和对象的区别不大,请看第①条。

如同 str = "Hello", str是变量(也是对象),而"Hello"就是常量了,str变量指向"Hello"。

所说的字符串重新赋值,只不过是创建了新的字符串,让变量指向这个新的地址罢了。

‘贰’ Python对象的拷贝

Python赋值操作或函数参数传递,传递的永远是对象引用(即内存地址),而不是对象内容。在Python中一切皆对象,对象又分为可变(mutable)和不可变(immutable)两种类型。对象拷贝是指在内存中创建新的对象,产生新的内存地址。当顶层对象和它的子元素对象全都是immutable不可变对象时,不存在被拷贝,因为没有产生新对象。浅拷贝(Shallow Copy),拷贝顶层对象,但不会拷贝内部的子元素对象。深拷贝(Deep Copy),递归拷贝顶层对象,以及它内部的子元素对象。

Python中一切皆对象,对象就像一个塑料盒子, 里面装的是数据。对象有不同类型,例如布尔型和整型,类型决定了可以对它进行的操作。现实生活中的"陶器"会暗含一些信息(例如它可能很重且易碎,注意不要掉到地上)。

对象的类型还决定了它装着的数据是允许被修改的变量(可变的mutable)还是不可被修改的常量(不可变的immutable)。你可以把不可变对象想象成一个透明但封闭的盒子:你可以看到里面装的数据,但是无法改变它。类似地,可变对象就像一个开着口的盒子,你不仅可以看到里面的数据,还可以拿出来修改它,但你无法改变这个盒子本身,即你无法改变对象的类型。

对象拷贝是指在内存中创建新的对象,产生新的内存地址。

浅拷贝(Shallow Copy),拷贝顶层对象,但不会拷贝内部的子元素对象。
2.1.1. 顶层是mutable,子元素全是immutable
当顶层对象是mutable可变对象,但是它的子元素对象全都是immutable不可变对象时,如[1, 'world', 2]

① 创建列表对象并赋值给变量a

② 导入模块,使用.()函数浅拷贝a,并赋值给变量b

③ 修改变量a的子元素a[0] = 3,由于整数是不可变对象,所以并不是修改1变为3,而是更改a[0]指向对象3

当顶层对象是 mutable可变对象 ,但子元素也存在 mutable可变对象 时,如 [1, 2, ['hello','world']]

① 浅拷贝 .() 只拷贝了顶层对象,没有拷贝子元素对象['hello','world'],即a[2]和b[2]指向同一个列表对象

② 修改a[2][1] = 'china',则b[2][1] = 'china'

当顶层对象是immutable不可变对象,同时它的子元素对象也全都是immutable不可变对象时,如(1, 2, 3)

变量a与变量b指向的是同一个元组对象,没有拷贝

当顶层对象是immutable不可变对象时,但子元素存在mutable可变对象时,如(1, 2, ['hello','world'])

变量a与变量b指向的是相同的元组对象,并且a[2]与b[2]指向同一个列表,所以修改a[2][1]会影响b[2][1]

深拷贝(Deep Copy),递归拷贝顶层对象,以及它内部的子元素对象

当顶层对象是mutable可变对象,但是它的子元素对象全都是immutable不可变对象时,如[1, 'world', 2]

变量a与变量b指向不同的列表对象,修改a[0]只是将列表a的第一个元素重新指向新对象,不会影响b[0]

当顶层对象是mutable可变对象,但子元素也存在mutable可变对象时,如[1, 2, ['hello','world']]

深拷贝既拷贝了顶层对象,又递归拷贝了子元素对象,所以a[2]与b[2]指向了两个不同的列表对象(但是列表对象的子元素初始指定的字符串对象一样),修改a[2][1] = 'china'后,它重新指向了新的字符串对象(内存地址为140531581905808),不会影响到b[2][1]

当顶层对象是immutable不可变对象,同时它的子元素对象也全都是immutable不可变对象时,如(1, 2, 3)

变量a与变量b指向的是同一个元组对象,不存在拷贝

当顶层对象是immutable不可变对象时,但子元素存在mutable可变对象时,如(1, 2, ['hello','world'])

变量a与变量b指向的是不同的元组对象,同时a[2]与b[2]指向不同的列表对象,所以修改a[2][1]不会影响b[2][1]

使用=是赋值,即将列表对象的引用也赋值给变量b,可以将列表对象想象成一个盒子,变量a相当于这个盒子上的标签,执行b = a后,相当于再在这个盒子上贴上b标签,a和b实际上指向的是同一个对象。因此,无论我们是通过a还是通过b来修改列表的内容,其结果都会作用于双方。

b/c/d都是a的复制,它们都指向了不同的列表对象,但是没有拷贝子元素,a[2]和b[2]/c[2]/d[2]指向同一个列表, 相当于浅拷贝的效果

使用分片[:]操作,a和b其实是指向同一个元组,而且没有拷贝子元素,a[2]和b[2]也指向同一个列表,相当于浅拷贝的效果

同列表类似,可以使用字典的()函数或者转换函数dict()

变量a与变量b/c指向不同的字典,但是没有拷贝子元素,a['jobs']和b['jobs']/c['jobs']指定同一个列表, 相当于浅拷贝的效果

同列表类似,可以使用集合的()函数或者转换函数set()

变量a与变量b/c指向不同的集合,而集合的元素必须是hashable,所以修改集合a不会影响到b/c

‘叁’ Python不可变对象元组(tuple)详解

元组和列表饥肢配很相似,不过元组是不能在原处改变的对象,这点性质和字符串一致,虽然元组不支持任何方法调用,但是元组具有列表的大多数属性,以下是元组的一些特性。

示例结果:

示例结果:

示例结果:

示例结果:

示例结果:

当我们在学习元组时,总是会纳闷烂指已经有了列表为什么还需要学习使用元组看似鸡肋的内置对象呢? 我们可以通过元组的不可变性运用在一些场景中,因为其不可变性本身本身就提供了某种完整性,比如使用元组来实现函数的多返回值饥链,作为参数传递给函数调用、或是从函数调用那里获得参数时,保护其内容不被外部接口修改。

‘肆’ Python 中的可变类型对象和不可变类型是什么意思

不可变数据类型对象是指,当一个对象创建成功后,该变量就记录携肢了一个常量值在内存中的地址.当对该不可变对象进行赋值时,并没有改变对象所代表的常量值,而是重新记录了被赋值卖隐慧对象在内存中的地址,
可变数据类型对象可以理解成是一个容器,在这个容器中,可以承载多个相同或不同的数据.并且,容器中的数据可以被替换修改等操中答作.
Python的可变型和不可变类型知道是什么了吧,如果学习Python不知道去哪里找学习资料,可以看黑马程序员,有学习资料、视频、技术等等!

‘伍’ python 里的可变对象与不可变对象具体怎么理解

Python的数据类型分为可变(mutable)与不可变(immutable)。不可变类型包含字符串(str),整数(int),元组(tuple);可变类型包含列表(list),字典(dict)。
是否为可变类型在于内存单元的值是否可以被改变。如果是内存单元的值不可改变的,在对对象本身操作的时候,必须在内存的另外地方再申请一块内存单元(因困迟为老的内存单元不可变),老链孙的内存单元就丢弃了(如果还有其他ref,则ref数字减1,类似unix下的hard-link);如果是可变的,对对象操作的时候,棚尺链不需要再在其他地方申请内存,只需要在此对象后面连续申请(+/-)即可,也就是它的地址会保持不变,但区域会变长或者变短。

‘陆’ Python,明明只append了一次,为什么所有子列表都变了啊.jpg

a 和 b 有何不同?有的同学可能会觉得这两种方法的结果是一样的,都是:

然而真实的结果是:

为什么会产生不同的结缺吵神果呢?明明只append了一次,为什么 a 中的所有子列表都变了呢?这要从 Python 的数据模型说起。

Python 中每个对象都有各自的编号、类型和值。一个对象被创建后,它的编号就绝不会改变,你可以将其理解为该对象在内碰碰存中的地址。 Python 还提供了 is 运算符和 id() 函数用于比较和查看对象的编号。

is 运算符可以比较两个对象的编号是否相同; id() 函数能返回一个代表其编号的整型数。

在 Python 中有些对象的值可以改变,有些不可以。值可以改变的对象被称为可变对象;值不可以改变的对象就被称为不可变对象。(一个不可变容器对象如果包含对可变对象的引用,当后者的值改变时,前者的值也会改变;但是该容器仍属于不可变对象,因为它所包含的对象集是不会改变的。因此,不可变并不严格等同于值不能改变,实际含义要更微妙。) 一个对象的可变性是由其类型决定的;例如,数字、字符串和元组是不可变的,而字典和列表是可变的。 [1]

在一开始的问题中,列表 a 初始化的时候,内层的子列表 [] 实际上是引用了同一个可变对象。因此对内层的任意一个子列表的 append() 操作,最终会因为引用了同一个对象的原因,体现在每一个子列表上。

而 b 初始化的时候是创建了3个不同 [] 对象。每个子列表引用了不同的对象。

在 Python 的官方文档中也说明了这种情况, lst * n 这种形式相当于 lst 与自身进行 n 次拼接。 lst 中的项不会被拷贝,而是会进行多次引用。 [2]

我们再深入的看一下刚才括号里伏亏面很拗口的那句话:

“一个不可变容器对象如果包含对可变对象的引用,当后者的值改变时,前者的值也会改变;但是该容器仍属于不可变对象,因为它所包含的对象集是不会改变的。因此,不可变并不严格等同于值不能改变,实际含义要更微妙。”

简单做个实验:

我们可以看到 a 、 b 是可变对象,而 c 是不可变容器对象。我们改变了 a 的值,可以看到 a 的编号没有发生改变,这个很正常,因为 a 是可变对象。
同时因为 c 中的元素包括 a ,所以 c 的“值”也发生了变化,但是 c 的编号没有变化,也就是说还是那个不可变对象。也就是说 c 的不变性是基于它的对象集没有变化。

‘柒’ Python中的元组(Tuple)

在Python中,元组(Tuple)与列表(List)类似,也是一个有序的序列,但元组是一个不可变对象,其一旦初始化后便无法进行修改。

一般在创建元组时,分为创建空元组和非空元组,其创建方式如下:

从上面可以看到,我们只需要把元组的所有元素放在 小括号 () 里面,每个元素之间通过 逗号 , 间隔起来即可,当然和列表一样,元组中同样允许存放不同数据类型的元素。

元组中使用的 () 是常见的数学运算符,如果参考列表的创建,如下:

从上面可以看到,我们创建出来的压根就不是元组类型。那么我们想创建一个只包含一个元素的元组时,需要怎么创建呢?

很简单,只需要在元素后加上一个 逗号 , ,这样创建出来就是一个元组。

当我们需要访问元组中的元素,可以类似列表一样通过索引取值或切片取值。

如果访问的索引不存在,那么会报错: IndexError: tuple index out of range

在元组中,可以像列表一样使用切片,语法为: 元组[start:end:step] ,通过切片可以获取到元组中指定范围的元素,返回结果是一个新元组。在这里,需要注意切片的规则是左闭右开,也就是说包含头部 start,但不包含尾部 end ,而 step 则表示切片的步长,需要每间隔 step 个元素取一次。

在上面我们提到了元组是不可变的,这就意味着我们创建一个元组之后,不能再去改变其值了,比如下面这样就会出现报错:

假设元组中存在元素是一个可变对象,如果我们不改变元组,但改变了可变对象的值,又会得到怎么的结果呢?请看下面实例:

从上面可以看到,元组中的第 2 个元素似乎发生了变化,这岂不是和上面说的元组不可变自相矛盾了吗?

其实,我们说的元组不可变,指的是元组中各元素的指向永远保持不变。在上面操作中,元组中第 2 个元素指向的一直是 列表nums ,我们改变的其实不是元组的元素,而只是改变了 列表nums 中的元素。

元组是不可变的,因此我们就不能删除元组中的元素,但是,我们可以通过 del 语句删除整个元组。

通过关键字 in ,可检查当前元组中是否包含指定元素,返回结果为布尔值 True 或 False。

通过关键字 in ,还可以用于遍历当前元组。

‘捌’ Python3 & 浅拷贝与深拷贝

在Python中对象的赋值(=)其实就是对象的引用。即:当创建一个对象,把它赋值给另一个变量时,python并没有拷贝这个对象,只是拷贝了这个对象的引用而已。

Python中对象的拷贝分为:浅拷贝()和深拷贝(deep)。
浅拷贝:拷贝了最外围的对象本身,内部的元素都只是拷贝了一个引用而已。也就是,将原对象在内存中引用地址拷贝过来,然后让新的对象指向这个地址。可以使用“=”或列表自带的()函数(如list.()),或使用模块的()函数。

深拷贝:外围和内部元素磨碰都进行了拷贝对象本身,而不是引用。即把对象复制一遍,并且该对象中引用的其他对象也同时复制,完全得到一个新的一模一样的对象,对新对象里的值进行修改不会影响原有对象,新对象和原对象完全分离开。深拷贝只能使用模块中deep()函数,使用前要含高导入:from import deep。

Python中对象分为不可变对象 、可变对象。
不可变对象:一旦创建就不可修改的对象,例如:字符串、元组、数字
可变对象:可以修改的对象,例如:列表、字典。
其中Python中的切片可以应用于:列表、元组、字符串,但不能应用于字典。
而深浅拷贝,可应用于序列(列表、元组、字符串),也可应用于字典。
其中不可变对象,不管是深拷贝还是浅拷贝,地址值在拷贝后的值都是一样的。

以下以元组(不可变类型)为例

从上述示例可以看出:
不可变对象类型,没有被拷贝的说法,即便是用深拷贝,查看id的话也是一样的,如果对其重新赋值,也只是新创建一个对象,替换掉旧的而已。
所以不可变类型,不管是深拷贝还是浅拷贝,地址值和拷贝后的值都是一样的。

以下以列瞎老谈表(可变类型)为例
第一种方法:使用=号浅拷贝

输出结果:

第二种方法:使用浅拷贝

输出结果:

第三种方法:使用deep深拷贝

输出结果:

从上述示例可以看出:
=浅拷贝:值相等,地址相等
浅拷贝:值相等,地址不相等
deep深拷贝:值相等,地址不相等

总结:
1,深浅拷贝都是对源对象的复制,占用不同的内存空间。
2,不可变类型的对象,对于深浅拷贝毫无影响,最终的地址值和值都是相等的。
3,可变类型的对象,使用=浅拷贝时, 值相等,地址相等,对新对象里的值进行修改同时会影响原有对象;使用浅拷贝时值相等,地址不相等;使用deep深拷贝时值相等,地址不相等。可以看出针对可变类型浅拷贝和deep深拷贝,对新对象里的值进行修改不会影响原有对象。

‘玖’ python-进阶-对象变动 a += 1与a = a+1区别

Python中 可变(mutable)与不可变(immutable)的数据类型 让新手很是头辩伏痛。简单的说,可变(mutable)意味着"可以被改动",而不可变(immutable)的意思是“常量(constant)”。想把脑筋转动起来吗?考虑下这个例子:

刚刚发生了什么?我们预期的不是那样!

这不是一个bug。这是对象可变性(mutability)在作怪。 每当你将一个变量赋值为另一个可变类型的变量时,对这个数据的任意改动会同时反映到这两个变量上去。新变量只不过是老变量的一个别名而已。这个情况只是针对可变数据类型

啊哈!这次又没有达到预期, 是列表的可变性在作怪 。在Python中当函数被定义时,默认参数只会运算一次,而不是每次被调用时都会重新运算。你应该永远不要定义可变类型的默认参数,除非你知道你正在做什么。你应该像这样做:

现在每当你在调用这个函数不传入target参数的时候,一个新的列表会被创建。举个例子:

为什么会出现这样?

a+=b

a=a+b

显然,两者是有区别的,而这种区别只出现在可变对象上(为什么携扒携是可变对象后面再说),是什么原因造成了两者的区别呢?

+= 操作调用 __iadd__ 方法,没有该方法此行时,再尝试调用 __add__ 方法

__iadd__ 方法 直接在原对象a1上进行更新,该方法的返回值为None

+ 操作调用 __add__ 方法

__add__ 方法会返回一个新的对象,原对象不修改,因为这里 a1被重新赋值了,a1指向了一个新的对象,所以出现了文章开头a1不等于a2的情况

为什么前面我说这种差异只会发生的 可变对象 身上?因为对于不可变对象,根本没有 __iadd__ 方法,所以 += 和 + 的效果是一样的,因为调的都是 __add__ 方法

‘拾’ Python 的传参是传值还是传址

Python对可变对象(字典或列表)传址,
对不可变对象(数字、字符或元祖)传值。

阅读全文

与不可变对象python相关的资料

热点内容
女主在军校很厉害的小说 浏览:516
米思齐为什么要编译才有效 浏览:283
法人如何用app分配权限 浏览:486
安卓手机游戏怎么导入苹果 浏览:537
什么是配置文件java 浏览:646
男主体质特殊只能和女主 浏览:47
中国算法和别的国家不一样吗 浏览:517
法国电影《狂怒》未删减版 浏览:154
金万维无法pin通服务器地址 浏览:825
linuxc语言定时 浏览:25
没有没网址不卡的 浏览:575
中国一共多少程序员 浏览:181
毒液几部可以独立看吗 浏览:426
git解压版配置 浏览:944
易语言高仿steam源码 浏览:204
strerror源码 浏览:118
PDF档名称 浏览:872
男主叫地的原始部落电影 浏览:291
免费的pdf加水印 浏览:739
网游解压卡 浏览:659