-
2020-12-06 12:34:31
Python中一切皆对象,每个对象都有其唯一的id,对应的类型和值,其中id指的是对象在内存中的位置。根据对象的值是否可修改分为可变对象和不可变对象。其中,
不可对象包括:数字,字符串,tuple
可变对象包括:list,dict,set
Python中的变量可以指向任意对象,可以将变量都看成是指针,保存了所指向对象的内存地址(对象的引用)。
不可变对象
对于不可变对象,如果要更新变量引用的不可变对象的值,会创建新的对象,改变对象的引用,举个例子:
In [41]: x = 1
In [42]: y = x
In [43]: print(id(x))
140719461487648
In [44]: x = 2
In [45]: print(id(y))
140719461487648
In [46]: print(id(x))
140719461487680
In [47]: print(id(2))
140719461487680
上述是int类型的一个实例,可以看到:
想要变量的值,会在内存中创建一个新的对象,变量指向新的对象。
对于值为1或者2,不管几个引用指向它,内存中都只占用了一个地址,在Python内部会通过引用计数来记录指向该地址的引用个数,当引用个数为0时会进行垃圾回收。
所以,不可变对象的优点是对于相同的对象,无论多少个引用,在内存中只占用一个地址,缺点是更新需要创建新的对象,因此效率不高。
可变对象
对于可变对象,举个例子:
In [57]: a = [1, 2]
In [58]: b = a
In [59]: print(id(a), id(b))
1961088949320 1961088949320
In [60]: a.append(3)
In [61]: print(a, b)
[1, 2, 3] [1, 2, 3]
In [62]: print(id(a), id(b))
1961088949320 1961088949320
In [63]: a = [1, 2, 3]
In [64]: print(id(a))
1961088989704
可以看到:
值的变化是在原有对象的基础上进行更新的,变量引用的地址没有变化。
对于一个变量的两次赋值操作,值相同,但是引用的地址是不同的,也就是同样值的对象,在内存中是保存了多份的,地址是不同的。
注意,我们研究可变对象的变化,研究的是同一对象,也就是可变指的是append, +=这种操作,而不包括新的赋值操作,赋值操作是会新建一个对象的。比如:
In [96]: a = [1, 2, 3]
In [97]: b = a
In [98]: a = [1]
In [99]: b
Out[99]: [1, 2, 3]
参数传递问题
因为可变对象和不可变对象的特性,因此在参数传递上需要注意,详情可参考 我的回答
深拷贝和浅拷贝
首先,举个例子:
In [69]: data = [{'name': 'a', 'deleted': True}, {'name' : 'b', 'deleted': False}, {'name': 'c', 'deleted': False}]
In [70]: print(data)
[{'name': 'a', 'deleted': True}, {'name': 'b', 'deleted': False}, {'name': 'c', 'deleted': False}]
In [71]: def add(data_list):
...: for item in data_list:
...: if item.get('deleted'):
...: data_list.remove(item)
...: return data_list
...:
In [72]: add_result = add(data)
In [73]: print(add_result)
[{'name': 'b', 'deleted': False}, {'name': 'c', 'deleted': False}]
In [74]: print(data)
[{'name': 'b', 'deleted': False}, {'name': 'c', 'deleted': False}]
你会发现调用了add方法之后,data已经变了,在之后的代码中你已经无法再使用原来的data了,具体的原因在参数传递那个问题中我有说明。
但是,当你希望在add方法中并不会修改data的值,要怎么做呢?
这时候,你需要了解下深拷贝和浅拷贝:
深拷贝和浅拷贝的概念:
浅拷贝(shallow copy):构造一个新的对象并将原对象中的引用插入到新对象中,只拷贝了对象的地址,而不对对应地址所指向的具体内容进行拷贝,也就是依然使用原对象的引用。实现方式包括:工厂函数(list, set等)、切片,copy模块的copy方法。
深拷贝(deep copy):复制了对象的和引用,深拷贝得到的对象和原对象是相互独立的。实现方式:copy模块的deepcopy方法。
所以,上述代码可按需更新为:
def add(data_list):
ret_data_list = deepcopy(data_list)
for item in ret_data_list:
if item.get('deleted'):
ret_data_list.remove(item)
return ret_data_list
以上。
更多相关内容 -
python中不可变数据类型和可变数据类型
2021-01-14 14:54:00不可变数据类型: 当该数据类型的对应变量的值发生了改变,那么它对应的内存地址也会发生改变,对于这种数据类型,就称不可变数据类型。可变数据类型 :当该数据类型的对应变量的值发生了改变,那么它对应的内存地址...1.名词解释
以下所有的内容都是基于内存地址来说的。
不可变数据类型: 当该数据类型的对应变量的值发生了改变,那么它对应的内存地址也会发生改变,对于这种数据类型,就称不可变数据类型。
可变数据类型 :当该数据类型的对应变量的值发生了改变,那么它对应的内存地址不发生改变,对于这种数据类型,就称可变数据类型。
总结:不可变数据类型更改后地址发生改变,可变数据类型更改地址不发生改变
2.数据类型分类
在python中数据类型有:整型,字符串,元组,集合,列表,字典。接下来我们用例子来一一查看他们分别属于不可变数据类型还是可变数据类型。
2.1 整型
a = 1
print(id(a),type(a))
a = 2
print(id(a),type(a))
1912499232
1912499264
我们可以发现,当数据发生改变后,变量的内存地址发生了改变,那么整型就是不可变数据类型。
2.2 字符串
b = 'djx'
print(id(b),type(b))
b = 'djx1996'
print(id(b),type(b))
535056476344
535056476624
我们可以发现,当数据发生改变后,变量的内存地址发生了改变,那么字符串就是不可变数据类型。
2.3 元组
元组被称为只读列表,即数据可以被查询,但不能被修改,但是我们可以在元组的元素中存放一个列表,通过更改列表的值来查看元组是属于可变还是不可变。
c1 = ['1','2']
c = (1,2,c1)
print(c,id(c),type(c))
c1[1] = 'djx'
print(c,id(c),type(c))
result:
(1, 2, ['1', '2']) 386030735432
(1, 2, ['1', 'djx']) 386030735432
我们可以发现,虽然元组数据发生改变,但是内存地址没有发生了改变,但是我们不可以以此来判定元组就是可变数据类型。我们回头仔细想想元组的定义就是不可变的。我们修改了元组中列表的值,但是因为列表是可变数据类型,所以虽然在列表中更改了值,但是列表的地址没有改变,列表在元组中的地址的值没有改变,所以也就意味着元组没有发生变化。我们就可以认为元组是不可变数据类型,因为元组是不可变的。
2.4 集合
集合我们常用来进行去重和关系运算,集合是无序的。
s = {1,'d','34','1',1}
print(s,type(s),id(s))
s.add('djx')
print(s,type(s),id(s))
result:
{'d', 1, '34', '1'} 870405285032
{1, '34', 'djx', '1', 'd'} 870405285032
2.5 列表
列表是python中的基础数据类型之一,其他语言中也有类似于列表的数据类型,比如js中叫数组,它是以[]括起来,每个元素以逗号隔开,而且它里面可以存放各种数据类型。
list = [1,'q','qwer',True]
print(list,type(list),id(list))
list.append('djx')
print(list,type(list),id(list))
result:
[1, 'q', 'qwer', True] 808140621128
[1, 'q', 'qwer', True, 'djx'] 808140621128
虽然列表数据发生改变,但是内存地址没有发生了改变,那么列表就是可变数据类型。
2.6 字典
字典是python中唯一的映射类型,采用键值对(key-value)的形式存储数据。python对key进行哈希函数运算,根据计算的结果决定value的存储地址,所以字典是无序存储的。但是在3.6版本后,字典开始是有序的,这是新的版本特征。
字典的key值可以是整型,字符串,元组,但是不可以是列表,集合,字典。
tuple = (1)
dic = {1:2}
d = { tuple:1,'key2':'djx','key3':'li'}
print(d,type(d),id(d))
d['key4'] = 'haha'
print(d,type(d),id(d))
result:
{1: 1, 'key2': 'djx', 'key3': 'li'} 256310956320
{1: 1, 'key2': 'djx', 'key3': 'li', 'key4': 'haha'} 256310956320
虽然字典数据发生改变,但是内存地址没有发生了改变,那么字典就是可变数据类型。
图片.png
-
python中列表长度可变吗
2021-01-13 04:24:23python中列表长度可变,可变类型指的是内存id不变,type也不变的前提下,value是否是可变的。列表和字典都是可变类型,对于可变对象,比如list,对list进行操作,list内部的内容是会变化的。python中列表长度可变吗...python中列表长度可变,可变类型指的是内存id不变,type也不变的前提下,value是否是可变的。列表和字典都是可变类型,对于可变对象,比如list,对list进行操作,list内部的内容是会变化的。
python中列表长度可变吗?下面给大家介绍一下python中可变和不可变的类型:
可变、不可变
可变/不可变类型,指的是:内存id不变,type也不变的前提下,value是否是可变的。
int()和str()都是不可变类型
列表、字典是可变类型
对于可变对象,比如list,对list进行操作,list内部的内容是会变化的,比如:
>>> a = ['c', 'b', 'a']>>> a.sort()>>> a
['a', 'b', 'c']
而对于不可变对象,比如str,对str进行操作呢:
>>> a = 'abc'>>> a.replace('a', 'A')'Abc'>>> a'abc'
虽然字符串有个replace()方法,也确实变出了'Abc',但变量a最后仍是'abc',应该怎么理解呢?
我们先把代码改成下面这样:
>>> a = 'abc'>>> b = a.replace('a', 'A')>>> b'Abc'>>> a'abc'
要始终牢记的是,a是变量,而'abc'才是字符串对象!有些时候,我们经常说,对象a的内容是'abc',但其实是指,a本身是一个变量,它指向的对象的内容才是'abc':
当我们调用a.replace('a', 'A')时,实际上调用方法replace是作用在字符串对象'abc'上的,而这个方法虽然名字叫replace,但却没有改变字符串'abc'的内容。相反,replace方法创建了一个新字符串'Abc'并返回,如果我们用变量b指向该新字符串,就容易理解了,变量a仍指向原有的字符串'abc',但变量b却指向新字符串'Abc'了:
所以,对于不变对象来说,调用对象自身的任意方法,也不会改变该对象自身的内容。相反,这些方法会创建新的对象并返回,这样,就保证了不可变对象本身永远是不可变的。
阅读:次
-
Python中的可变对象和不可变对象
2020-12-14 09:39:17一、python中的变量与对象首先在理解python的可变对象和不可变对象时,要理解python的赋值操作。在python中,一切事物皆是对象,变量是对象在内存中的存储和地址的抽象对变量的理解在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 会报错的。
-
python中对象的可变与不可变
2020-12-09 05:34:02在python中一切皆对象,对象是对数据的抽象。所有对象有三个要素:类型、...对象一旦被创建,它的类型和标识是不变的,根据值的绑定关系是否可变分为可变对象和不可变对象。如果“值”理解为对象的内容容易产生疑... -
python的可变与不可变数据类型
2020-12-08 20:22:47首先,我们需要知道在python中哪些是可变数据类型,哪些是不可变数据类型。可变数据类型:列表list和字典dict;不可变数据类型:整型int、浮点型float、字符串型string和元组tuple。用一句话来概括上述过程就是:... -
python中的可变数据类型与不可变数据类型
2021-02-10 10:41:15一、定义:不可变数据类型: 当该数据类型...总结:不可变数据类型更改后地址发生改变,可变数据类型更改地址不发生改变二、数据类型的分类数据类型可变/不可变整型不可变字符串不可变元组不可变列表可变集合可变字... -
Python中的不可变类型有哪些
2021-02-23 09:20:12不可变类型: 不允许在原有内存空间的基础上修改数据,修改后数据的内存地址会发生变化 不可变类型: 数字,字符串,元组,range my_str = "abc" print("修改前:", my_str, id(my_str)) # 错误写法 # my_str[-1] ... -
Python-5 可变类型与不可变类型
2020-11-30 08:58:373、在字典中,不可变类型可作key值:infor = {"name":"zhangsan",3.14:"pai",(100,99):"score"}可变类型不可作key值。例1:a = 100def test(num):num+=numprint(num)test(a)print(a)#实际输出结果>200>100#因为a为不... -
python中哪些数据类型是可变序列,哪些数据类型是不可变序列?
2019-07-22 16:17:16有序序列有:列表、元组、字符串 无序序列有:字典、集合 可变序列有:列表、字典、集合 不可变序列有:字符串、元组 -
python集合是可变类型吗
2020-12-20 11:12:14python 集合的元素为什么不能是可变数据类型集合(set)是可以变的,它是一个无序不重复元素集 元组(touple)才是不可变的Python 中的可变类型对象和不可变类型是什么意思?Python3 中有六个标准的数据类型(内置数据类型... -
python中的可变对象与不可变对象
2018-12-20 22:40:20python与C/C++不一样,它的变量使用有自己的特点,当初学python的时候,一定要记住“一切皆为对象,一切皆为对象的引用”这句话...首先,我们需要知道在python中哪些是可变数据类型,哪些是不可变数据类型。可变数据... -
python中列表可变化吗
2020-12-10 10:45:56以下所有的内容都是基于内存地址来说的。不可变数据类型: 当该数据类型的对应变量的值发生了改变,那么...总结:不可变数据类型更改后地址发生改变,可变数据类型更改地址不发生改变列表是python中的基础数据类型之... -
python的可变类型与不可变类型
2020-12-04 04:47:18首先,我们需要知道在python中哪些是可变数据类型,哪些是不可变数据类型。可变数据类型:列表list和字典dict;不可变数据类型:整型int、浮点型float、字符串型string和元组tuple。用一句话来概括上述过程就是:... -
为什么Python中字典的key必须是不可变的?
2020-12-22 11:03:57为什么字典的key必须是不可变的?字典的哈希表实现使用从键值计算的哈希值来查找键。如果键是可变对象,则其值可能会发生变化,因此其哈希值也会发生变化。但是,由于无论谁更改键对象都无法判断它是否被用作字典... -
详解Python的三种可变参数
2020-12-08 10:31:25详解Python的三种可变参数可变参数可变参数应该最简单,在C/C++和Java等语言中都有,就是用*号来表示,例如def testArg(*arg)你可以传入任意多个元素(包括0)到参数中,在函数内部会自动认为是一个元组或列表关键字... -
python集合中元素类型不可以是_python中集合可变吗
2021-01-30 05:44:34python集合分为两种类型:(推荐学习:Python视频教程)set —— 可变集合,集合中的元素可以动态的增加或删除。frozenset —— 不可变集合,集合中的元素不可改变。注:对于 并集, 交集, 差集 等的返回值,与最... -
python 可变与不可变变量
2019-03-09 19:17:28python中不存在所谓的传值调用,一切传递的都是对象的引用,也可以认为是传址。可变变量和不可变变量。为什么python函数参数不会被修改 -
python中set集合属于可变类型吗
2021-01-13 11:22:51什么是可变/不可变对象不可变对象,该对象所指向的内存中的值不能被改变。当改变某个变量时候,由于其所指的值不能被改变,相当于把原来的值复制一份后再改变,这会开辟一个新的地址,变量再指向这个新的地址。可变... -
python的不可变数据类型
2020-11-20 08:18:11python的不可变数据类型是指 当对应其数据类型的变量发生改变时 变量的内存地址也发生改变 注:python不存储变量的值,而存储变量的内存地址或引用 -
python基础(五)python中的可变数据类型和不可变数据存储原理
2019-06-12 22:13:19(一)python中的不可变数据类型 a.不可变数据类型有数值型、字符型、元组。 b.存储原理:不允许变量的值发生变化,如果改变了变量的值,相当于是新建了一个对象新开辟一个内存区来存放该值;而对于相同值... -
python中可变集合set和不可变集合frozenset的区别
2018-10-19 18:09:26frozenset —— 不可变集合。集合中的元素不可改变。 注:对于 并集, 交集, 差集 等的返回值,与最左边的操作数具有相同的类型。例如:s & t 取交集。s集合是一个set类型的集合,t集合是一个f... -
Python中的可变数据类型和不可变数据类型
2019-05-14 17:13:25Python的基本数据类型大致可分为6类:1.Number(数字) 2. String(字符串) 3. Tuple (元组) 4. List(列表) 5. Dictionary (字典) 6....这6种类型又可以分为可变类型和不可变类型,其中不可变类型有:Number(数字) S... -
Python数据类型有哪些,哪些是可变类型,哪些是不可变类型?
2021-01-31 20:06:53Python数据类型有哪些,哪些是可变类型,哪些是不可变类型? 类型: 整数int 字符串str 浮点数float 布尔型bool 列表list 字典dict 集合set 元组tuple 可变类型:就是这个数据类型的值在不改变这一块内存空间,而去... -
python字典中的key可以是可变类型吗
2020-11-30 08:57:58python字典中的key可以是可变类型吗发布时间:2020-11-09 17:07:39来源:亿速云阅读:76...python中字典的key不能是可变类型。字典可存储任意类型对象,其中值可以取任何数据类型,但键必须是不可变的,如字符串... -
python可变数据类型和不可变数据类型
2019-01-06 23:44:46有很多人在刚开始接触 python 这个语言的时候,总是搞不清楚什么是 可变数据类型 什么是 不可变数据类型,今天就让我们通过这篇小文章来详细了解一下。 知识点目录: 1.python 的数据类型 2.可变数据类型 和 不可...