前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Python 基础系列--可变/不可变的数据类型

Python 基础系列--可变/不可变的数据类型

作者头像
somenzz
发布2020-12-10 11:25:47
1.3K0
发布2020-12-10 11:25:47
举报
文章被收录于专栏:Python七号Python七号

数字、字符串、列表、元组、字典、集合是 Python 的六种标准数据类型,每一个 Python 程序都必然有这些数据类型的应用,如果会熟练使用数据类型,基本上 Python 编程已经会了一半了。

首先来看一下 Python 标准数据类型的分类:

Python 6个标准数据类型.png

这里就有必要了解下什么是可变数据类型,什么是不可变数据类型,这对理解使用函数是否会改变传入的参数的值非常重要,也可避免因数据类型导致的程序 bug。

不可变数据类型

不可变数据类型是:变量所向的内存地址处的值是不可以被改变的。你可能不太理解上面这句话,那么接着往下看。

python 世界里,一切皆为对象(object),任何变量都是对象的引用。因此不用担心 Python 有类似于 C/C++ 中指针的复杂问题。以不可变数据类型中的整数(int)为例: 随便选取一个整数,例如 18 ,在 python 中 id(18) 来获得 18 在内存中的地址。

代码语言:javascript
复制
>>> id(18)
1409838640
>>> x=18
>>> id(x)
1409838640
>>> y=18
>>> id(y)
1409838640
>>> z=18
>>> id(z)
1409838640
>>>

如上所示,在某一次 Python 程序执行的过程中,变量 18 在内存中的地址为 1409838640,你定义 x = 18; y = 18; z = 18;无论定义多少个变量,只要它的值为 18 ,他们地址均为 1409838640,在 python 内部执行垃圾回收之前,地址 1409838640 处的值一直都是 18 ,不可被改变。

有人可能有疑问了:变量 x 是整数类型,直接给 x 赋值 19 不就改改变了 x 的值了,为什么说整数是不可变数据类型 ?

答:确实改变了 x 的值,但是请注意,Python 中一切皆为对象,任何变量都是对象的引用,因此当 x = 18 时,x 是对象 18 的引用;给 x 赋值 19 时,x 是新对象 19 的引用,x 的地址变为整数 19 的内存地址,内存地址1409838640 处的值仍是 18,请看下面的交互环境输出结果。

代码语言:javascript
复制
>>> x = 19
>>> id(x)
1409838672
>>> id(19)
1409838672
>>> id(18)
1409838640
>>> id(y)
1409838640
>>> id(z)
1409838640

可以看出执行 x = 19 后变量 x 的地址就是 19 的地址 1409838672,变量 y 和 z 仍指向整数 18 。内存中对于整数 18 只占用了一个地址,而不管有多少个引用指向了它,都只有一个地址值,只是有一个引用计数会记录指向这个地址的引用到底有几个而已。当变量 x,y,z 都指向 18 时,18 的引用计数就是 3,18 在内存中只有一份,当所有的变量都不指向 18 时,垃圾回收程序就会在适当的时机收回 18 , 收回 18 后,18 这个对象在内存中就中不存在了。

之所以说 x 是不可变数据类型,指的是 x 引用的地址处的值是不能被改变的,也就是 1409838640 地址处的值在没被垃圾回收之前一直都是 18,不能改变,如果要把 x 赋值为 19 ,那么只能将 x 引用的地址从 1409838640 变为 1409838672,相当于 x = 19 这个赋值操作又创建了一个对象,即 19 这个对象。所以说整数这个数据类型是不可变的,如果想对整数类型的变量再次赋值,在内存中相当于又创建了一个新的对象,而不再是之前的对象。其他不可变类型也是同样的道理。

注意:元组是个特例,值相同的元组的地址可能不同,因为它的本质是只读的列表。

可变数据类型

可变数据类型是:变量所向的内存地址处的值是可以被改变的。 以可变数据类型中的列表 list 为例,如果不知道 python 的列表也没关系,本文后面会介绍。 先看一段交互式环境中的输出

代码语言:javascript
复制
>>> x=[1,2,3]
>>> id(x)
2429731524424
>>> x=[1,2,3]
>>> id(x)
2429731548808

可以看出,对变量 x 执行两次同样的赋值操作,变量 x 的地址却不是同一个,这与不可变数据类型有明显的区别,其实两次赋值操作在内存中创建了两个不同的对象,因此对于可变类型,具有同样值的对象是不同的对象,他们彼此是独立的。

接下来我们来改变列表值,看一看变量地址的变化情况 。

代码语言:javascript
复制
>>> x=[1,2,3]
>>> id(x)
2429731548808
>>> y=[1,2,3]
>>> id(y)
2429731524424
>>> z=[1,2,3]
>>> id(z)
2429731565704
>>> x.append("a")
>>> x
[1, 2, 3, 'a']
>>> y
[1, 2, 3]
>>> z
[1, 2, 3]
>>> id(x)
2429731548808
>>> id(y)
2429731524424
>>> id(z)
2429731565704
>>>del x[2]
>>>x
[1,2,'a']
>>>id(x)
2429731548808

我们首先定义了三个变量 x,y,z ,分别赋值为 [1,2,3],但他们是不同的对象,因此在内存中的地址也不一样。当对变量 x 指向的列表增加一个元素 "a" 时,变量 x 的值发生的变化,但内存中的地址还和以前一样,变量 y ,z没有做任何操作,他们的值和地址都不变,后面删除列表中的第三个元素 x[2],同样发现 x 的地址仍没有变化。

因此可变数据类型是说对一个变量所指向的内存地址处的值是可以被变的,值的变化并不会引起新建对象,即地址是不会变的。

理解了可变数据类型和不可变数据类型,相信你非常容易解释如下现象:

代码语言:javascript
复制
>>> x=y=z=1
>>> x=2
>>> x,y,z                #这里,y 与 z 的值没有被改变
(2, 1, 1)
>>> a=b=c=[1,2,3]
>>> a.append('a')
>>> a,b,c            #这里,b 与 c 的值却被改变
([1, 2, 3, 'a'], [1, 2, 3, 'a'], [1, 2, 3, 'a'])

上一篇文章介绍了字符串及编码,本文介绍一下 Python 里最常用的 4种标准数据类型:列表,元组,字典、集合:

1.列表(list)

列表类似于 C 语言中的数组,是一种线性的数据结构,与 C 语言的数组不同地是,Python 中的列表可以存储不同的数据类型,列表内部也可以嵌套列表。 在 Python中,使用 "[]" 来定义一个列表,元素之间使用 "," 隔开。

代码语言:javascript
复制
>>> list1 = ['zhuang', 'xiao0.h','赤色彗星',]
>>> list2 =  [0,1,2,'abcd',[1,2,3]]
>>> list3 = ["上路", "中路", "下路"]
>>> list4 = [list1,list3]
>>> print(list1);
['zhuang', 'xiao0.h', '赤色彗星']
>>> print(list2);
[0, 1, 2, 'abcd', [1, 2, 3]]
>>> print(list3);
['上路', '中路', '下路']
>>> print(list4);
[['zhuang', 'xiao0.h', '赤色彗星'], ['上路', '中路', '下路']]
查询:访问列表元素、遍历

使用下标索引来访问列表中的值,同样你也可以使用方括号的形式截取字符,使用 for 循环遍历列表,如下所示:

代码语言:javascript
复制
('赤色彗星', ['赤色彗星'])
>>> list1[0]
'zhuang'
>>> list1[1]
'xiao0.h'
>>> list1[1:3]
['xiao0.h', '赤色彗星']
>>> for item in list1:
...     print(item)
...
zhuang
xiao0.h
赤色彗星
>>> for i,item in enumerate(list1):   # 如果需要使用下标就使用 enumerate(list),不推荐使用 len(list)
...     print(i,item)
...
0 zhuang
1 xiao0.h
2 赤色彗星
>>> for i in list4:
...     print(i)
...
['zhuang', 'xiao0.h', '赤色彗星']
['上路', '中路', '下路']
列表的增,删,改。
代码语言:javascript
复制
>>> list1
['zhuang', 'xiao0.h', '赤色彗星']
>>> list1.append("somenzz")   #增加值为 somenzz 的元素,放在最后位置
>>> list1
['zhuang', 'xiao0.h', '赤色彗星', 'somenzz']
>>>
>>> list1.append("somenzz1") #增加值为 somenzz1 的元素,放在最后位置
>>> list1.insert(0,"somenzz")  #增加值为 somenzz 的元素,放在第一位
>>> list1
['somenzz', 'zhuang', 'xiao0.h', '赤色彗星', 'somenzz', 'somenzz1']
>>> list1.remove('somenzz1')  #删除第一个值为 somenzz1 的元素 
>>> list1
['somenzz', 'zhuang', 'xiao0.h', '赤色彗星', 'somenzz']
>>> list1[4]='xxxxxxxx'     #修改第五个元素的值为 xxxxxxxx 
>>> list1
['somenzz', 'zhuang', 'xiao0.h', '赤色彗星', 'xxxxxxxx']
>>> list1.pop()   #删除最后一个元素
'xxxxxxxx'
>>> list1
['somenzz', 'zhuang', 'xiao0.h', '赤色彗星']
>>> list1.pop(0)   #删除指定元素,删除下标为 0 的元素 ,即第一个元素
'somenzz'
>>> list1
['zhuang', 'xiao0.h', '赤色彗星']
>>> del list1[1]    #删除指定元素,删除下标为 1 的元素 ,即第二个元素
>>> list1
['zhuang', '赤色彗星']
>>>
Python列表函数&方法

请使用 help(list) 来获取 Python 提供的所有关于列表的函数或方法。

代码语言:javascript
复制
>>>help(list)

2.元组(tuple)

Python 的元组与列表类似,不同之处在于元组的元素不能修改。元组使用小括号,列表使用方括号。元组创建很简单,只需要在括号中添加元素,并使用逗号隔开即可。

代码语言:javascript
复制
>>> a=("上路","中路","下路","中路",)
>>> a[0]
'上路'
>>> a[3]
'中路'
>>> a[0:3]
('上路', '中路', '下路')
>>> a[:]
('上路', '中路', '下路', '中路')
>>> a.count("中路")
2
>>> type(a)
<class 'tuple'>
>>> help(tuple)
Help on class tuple in module builtins:

class tuple(object)
 |  tuple() -> empty tuple
 |  tuple(iterable) -> tuple initialized from iterable's items
 |
 |  If the argument is a tuple, the return value is the same object.
 |
 |  Methods defined here:
 |
 |  __add__(self, value, /)
 |      Return self+value.
......
 |
 |  count(...)
 |      T.count(value) -> integer -- return number of occurrences of value
 |
 |  index(...)
 |      T.index(value, [start, [stop]]) -> integer -- return first index of value.
 |      Raises ValueError if the value is not present.

请注意:元组的元素不能修改,其实是指元组中的元素所指向的内存地址是不可更改的,如果元组的元素是可变数据类型,则该元素的值是可以改变的。请看下面的例子:

代码语言:javascript
复制
>>> b=("a","b",[1,2,3])
>>> id(b)
2429731461592
>>> id(b[-1])
2429737198920
>>> b[-1].append(4)
>>> b
('a', 'b', [1, 2, 3, 4])
>>> id(b)
2429731461592
>>> id(b[-1])   # b[-1] 就是 b[2]
2429737198920
>>>

从上面的结果可以看出元组 b 的第三个元素的值 [1,2,3] 变为了 [1,2,3,4] 但 b 的地址和 b[2] 的地址都没有变化。

3.字典(dict)

提到字典,我们会想到中华字典,英语词典,通过给定的单词(key),查找其含义(value),在字典里,要查找的单词(key)在字典里是唯一的,但不同的单词的含义(value)可能相同。Python里的字典就是键值对(key-value)组成的集合,且可存储任意类型对象。定义一个字典非常简单:使用一对花括号{}括起,键值对之间使用“,”分隔。如:

代码语言:javascript
复制
>>> dict = { 'hello':'你好','world':'世界',} #定义一个字典dict
>>> dict
{'hello': '你好', 'world': '世界'}
>>> dict['hello']
'你好'
>>> len(dict)  #计算字典元素个数,即键的总数。
2
>>>str(dict)   #输出字典,以可打印的字符串表示。
"{'hello': '你好', 'world': '世界'}"

字典值可以是任何的 python 对象,既可以是标准的对象,也可以是用户定义的,但键不行。两个重要的点需要记住: (1)不允许同一个键出现两次。创建时如果同一个键被赋值两次,后一个值会被记住,如下实例:

代码语言:javascript
复制
>>> dict = { 'hello':'你好','world':'世界','hello':'world'} #键hello的值被更新为world
>>> dict
{'hello': 'world', 'world': '世界'}

(2)键必须不可变,可以用数字,字符串或元组充当,而用列表就不行,即:键必须为不可变数据类型。如下实例:

代码语言:javascript
复制
>>> d = { 'a':1,'b':2, ['a']:'abc'} #键是列表,会报错
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'
遍历字典:
代码语言:javascript
复制
>>> d = { 'a':1, 'b':2, 'c':3, 'd':4, 'e':5, 'f':6 } #定义一个字典
>>> for key,value in d.items():  #d.items()方法返回一个键值对的元组(key,value)
...     print(key,value)
...
a 1
b 2
c 3
d 4
e 5
f 6
>>> for key in d:    #以键来取值
...     print(key,d[key])   #python强制缩进,与上一行比有4个空格
...
a 1
b 2
c 3
d 4
e 5
f 6
>>>

修改字典:

代码语言:javascript
复制
>>> d = { 'a':1, 'b':2, 'c':3, 'd':4, 'e':5, 'f':6 }
>>> d['b']='b'
>>> d
{'a': 1, 'b': 'b', 'c': 3, 'd': 4, 'e': 5, 'f': 6}

删除字典元素:可以删除单一的元素,也可以一次性删除所有元素,清空字典,显式地删除一个字典用del命令,如下所示:

代码语言:javascript
复制
>>> del d['b']   #删除键b
>>> d       #删除键b后
{'a': 1, 'c': 3, 'd': 4, 'e': 5, 'f': 6}
>>> d.clear()  #清空字典 
>>> d
{}
>>> del d    #删除字典
>>> d        #删除字典后,字典d已不存在
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'd' is not defined
字典的内置方法
代码语言:javascript
复制
help(dict)

4.集合(set)

集合 set 是一个无序不重复元素集,基本功能包括关系测试和消除重复元素.。集合对象还支持 union (联合), intersection (交), difference (差) 和 sysmmetric difference (对称差集)等数学运算。

在 Python 中可以使用 ”x in set” 来判断x是否在集合中,使用 ”len(set)” 来获取集合元素个数,使用 ”for x in set” 来遍历集合中的元素。但由于集合不记录元素位置,因此集合不支持获取元素位置和切片等操作。 下面举例说明:

代码语言:javascript
复制
>>> x=set('abcd')   #创建集合x由单个字符组成
>>> y=set(['a','bc','d',10])  #创建集合y,由列表的元素组成
>>> x,y                 #打印x,y
({'a', 'b', 'd', 'c'}, {'a', 'd', 10, 'bc'})
>>> x & y               #取交集 
{'a', 'd'}
>>> x|y                 #取并集
{'c', 'bc', 'd', 10, 'b', 'a'}
>>> x-y                 #差集,表示x里有,y里没有的
{'b', 'c'}
>>> x^y                #对称差集(项在x或y中,但不会同时出现在二者中)
{'bc', 'c', 10, 'b'}

集合操作也可以用以去重元素:

代码语言:javascript
复制
>>> a = [11,22,33,44,11,22]
>>> b = set(a)
>>> b
set([33, 11, 44, 22])

集合的基本操作有:

函数

说明

s.add('x')

# 添加一项

s.update([10,37,42])

# 在s中添加多项

s.remove('H')

#使用remove()可以删除一项:

len(s)

#set的长度

x in s

#测试x是否是s的成员

x not in s

#测试x是否不是s的成员

s.issubset(t)

#相当于s <= t测试是否s中的每一个元素都在t中

s.issuperset(t)

#相当于s >= t测试是否t中的每一个元素都在s中

s.union(t)

# 相当于s | t 返回一个新的set包含s和t中的每一个元素

s.intersection(t)

#相当于s&t 返回一个新的set包含s和t中的公共元素

s.difference(t)

#相当于s-t返回一个新的set包含s中有但是t中没有的元素

s.symmetric_difference(t)

#相当于s^t返回一个新的set包含s和t中不重复的元素

s.copy()

#返回集合s的一个浅复制

s.discard(x)

#如果在 set “s”中存在元素 x, 则删除

s.pop()

#删除并且返回 set “s”中的一个不确定的元素, 如果为空则引发 KeyError

s.clear()

#删除set “s”中的所有元素

详细信息请推荐使用 help(set) 命令获取。

总结:纸上得来终觉浅,绝知此事要躬行,请初学者多敲代码,多实践,才能深入理解。

(完)

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-10-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Python七号 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 不可变数据类型
  • 可变数据类型
  • 1.列表(list)
    • 查询:访问列表元素、遍历
      • 列表的增,删,改。
        • Python列表函数&方法
        • 2.元组(tuple)
        • 3.字典(dict)
          • 遍历字典:
            • 字典的内置方法
            • 4.集合(set)
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档