Python 1 - 内置类型 - 序列(1)

Python 1 - 内置类型 - 序列(1)

Python 中提供了 3 种基本的序列类型:listtuplerange。大家可能对这3中类型都比较熟悉。

一般我们认为 tuple类型是不可以改变的list,当然,这在日常使用中,并没有什么不对,表现出来的属性也能验证这个说法,但是,在Python的底层实现当中,tuplelist 是完全不同的两个类型,后面我们会对他们的不同之处加以分析。

语句range(n)大家在for in 语句中经常使用,一般作为index来遍历其他类型的迭代。我们在使用的时候一般认为range(start, stop, step)方法产生了一个以start开始,stop结束,以step为间隔的列表。但是实际上,range也是一种基本的序列类型,它也并不会返回一个我们认为的list

可变序列

不可变序列类型与可变序列的区别就是,可变类型没有实现对 hash() 内置函数的支持。这种对hash()的支持,可以让 tuple作为dict的键存在。

listbytearray就是可变序列。

不可变序列

不可变序列类型的对象一旦创建就不能再改变:如果对象包含了对其他对象的引用,其中的可变对象就是可改变的;但是一个不可变对象所直接引用的对象集是不能改变的。

str,tuple,bytes都是不可变对象。相应的不可变序列就有 tuplerange,它们都是可以被hash()所使用的。

关于可变对象和不可变对象官方给出的例子。

我们打算对元组的元素进行自增运算,所以我们使用了 +=

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

报错信息告诉我们,元组对象不支持元素赋值。
发生异常的原因是显而易见的:

  • 1 会与对象 a_tuple[0] 相加,而该对象为 (1),得到结果对象 2,
  • 但当我们试图将运算结果 2 赋值给元组的 0 号元素时就将报错,因为我们不能改变元组的元素所指向的对象。

在表层之处,以上增强赋值语句所做的大致是这样:

>>> result = a_tuple[0] + 1
>>> a_tuple[0] = result
Traceback (most recent call last):
  ...
TypeError: 'tuple' object does not support item assignment

我们打算给元组中的列表元素进行扩展。

>>> a_tuple = (['foo'], 'bar')
>>> a_tuple[0] += ['item']
Traceback (most recent call last):
  ...
TypeError: 'tuple' object does not support item assignment

由于上面的例子,我们知道赋值会报错,但是当我们查看 a_tuple[0]时发现,列表已经被改变了

>>> a_tuple[0]
['foo', 'item']

要明白为何会这样,你需要知道:

  • (a) 如果一个对象实现了 iadd 魔术方法,它会在执行 += 增强赋值时被调用,并且其返回值将用于该赋值语句;
  • (b) 对于列表来说,iadd 等价于在列表上调用 extend 并返回该列表。

因此对于列表我们可以说 += 就是 list.extend 的“快捷方式”:

>>> a_list = []
>>> a_list += [1]
>>> a_list
[1]

>>> result = a_list.__iadd__([1])
>>> a_list = result

a_list 所引用的对象已被修改,而引用被修改对象的指针又重新被赋值给 a_list。
赋值的最终结果没有变化,因为它是引用 a_list 之前所引用的同一对象的指针,但仍然发生了赋值操作

因此,在我们的元组示例中,发生的事情等同于:

>>>
>>> result = a_tuple[0].__iadd__(['item'])
>>> a_tuple[0] = result
Traceback (most recent call last):
  ...
TypeError: 'tuple' object does not support item assignment

iadd 成功执行,因此列表得到了扩充,但是虽然 result 指向了 a_tuple[0] 已经指向的同一对象,最后的赋值仍然导致了报错,因为元组是不可变的。

序列的通用方法

所有的序列,都支持以下的方法,而每个方法都支持我们在自定义序列类型上实现这些操作。

in/not in

in 或 not in 是用来判断元素是否存在于序列当中。

>>> a = [1,2,3]
>>> 1 in a
True
>>> b = (1,2,3)
>>> 1 in b
True
>>> c = range(10)
>>> 1 in c
True

+

符号 +是用来扩展序列的。而拼接不可变序列总是会生成新的对象。这意味着通过重复拼接来构建序列的运行时开销将会基于序列总长度的乘方。如果想降低开销,只能采用一些其他的方法。

>>> a = [1,2,3]
>>> a1 = [4,5]
>>> a+a1
[1, 2, 3, 4, 5]

操作 +其实调用的是 __add__ 或者 __radd__。它们接收一个操作数,并根据是否是可变序列来返回一个新的对象或者更改自己的数据。

*

符号*+的多次自身拼接。上面说了 __add__是用来实现扩展的,那 *实际调用的就是 __mul__() 或者 __rmul__()

>>> a = [1,2,3]
>>> a*3
[1, 2, 3, 1, 2, 3, 1, 2, 3]
>>> 3*a
[1, 2, 3, 1, 2, 3, 1, 2, 3]

这里其实会有一个在创建多维列表时会出现的问题,当你尝试创建一个二维列表时:[[1],[1],[1],[1]]

>>> a = [[1]] * 4
>>> a
[[1], [1], [1], [1]]
>>> a[0][0] = 2
>>> a
[[2], [2], [2], [2]]

我明明只给 a[0][0]赋值了,为什么整个列表中其他子列表的值都改变了呢?
这是因为使用 *创建列表时,执行重复操作并不是创建副本,而是创建对现有对象的引用。
所以,当我们创建多维列表时,使用列表推导式会更好。

>>> a = [[1] for i in range(4)]
>>> a
[[1], [1], [1], [1]]
>>> a[0][0] = 2
>>> a
[[2], [1], [1], [1]]

取 [i]

序列都支持从位置 i 处获取值,i 的范围是 [0,len(list))。

>>> a=[1,2,3]
>>> a[1]
2

[i] 操作调用的是 __getitem__(self, key)方法,接收的键应为整数和切片对象。
而负数索引的特殊解读是取决于这个方法的。

切片操作

通过 range()的切片操作,可以很容易的看出它们的关系。

>>> a = range(10)
>>> a[1:4]
range(1, 4)
>>> a[2:5:3]
range(2, 5, 3)

len, min, max, count

这些函数都是支持序列操作的。 len 既获取序列数据的长度,min 既获取最小值,max 既获取最大值,count 既用来获取序列中某个数据的个数。

可变序列类型的通用方法

l[i] = x

与取值 [i] 相对应的,可变序列的 [i] 可以被修改赋值。实际上调用的方法为 __setitem__

>>> a = [1, 2, 3]
>>> a
[1, 2, 3]
>>> a[0] = 2
>>> a
[2, 2, 3]

l[i:j] = x

与切片 [i:j] 相对应的,切片替换也是一样存在的。 l[i:j] = [i...j]。
注意:x 必须与它所替换的切片具有相同的长度,既len(x) == j-i

>>> a = [1,1,1,1,1,1]
>>> a
[1, 1, 1, 1, 1, 1]
>>> a[0:3] = [1,2,3]
>>> a
[1, 2, 3, 1, 1, 1]

del l[i:j]

等同于 l[i:j] = []

>>> a = [1,2,3]
>>> del a[0:1]
>>> a
[2, 3]

l.append(x)

将 x 添加到序列的末尾 (等同于 s[len(s):len(s)] = [x])

>>> a = []
>>> a.append(1)
>>> a
[1]

l.clear()

等同于 del l[:]

>>> a = [1,2,3,4]
>>> a.clear()
>>> a
[]

l.copy()

创建 s 的浅拷贝 (等同于 s[:]) ,浅拷贝指的是通过 a 的值来构建一个新的对象 b,而 b 呢是跟 a 有着相同值得对象。

>>> a = [1,2,3]
>>> b = a.copy()
>>> b
[1, 2, 3]
>>> id(a)
4504616584
>>> id(b)
4504337160

s.extend(t) 或 s += t

用 t 的内容扩展 s (基本上等同于 s[len(s):len(s)] = t)

>>> a = [1]
>>> b = [2,3]
>>> a+=b
>>> a
[1, 2, 3]
>>> a.extend(b)
>>> a
[1, 2, 3, 2, 3]
>>>

s.insert(i, x)

在由 i 给出的索引位置将 x 插入 s (等同于 s[i:i] = [x])。

>>> a = [1,2,3]
>>> b = [4,5]
>>> a.insert(0, b)
>>> a
[[4, 5], 1, 2, 3]
>>> a[0:0] = [b]
>>> a
[[4, 5], [4, 5], 1, 2, 3]

s.pop([i])

提取在 i 位置上的项,并将其从 s 中移除,返回被移除的值。
注意:可选参数 i 默认为 -1,因此在默认情况下会移除并返回最后一项。

>>> a
[[4, 5], [4, 5], 1, 2, 3]
>>> a.pop(1)
[4, 5]
>>> c = a.pop(1)
>>> c
1

s.remove(x)

删除 s 中第一个 s[i] 等于 x 的项目。

>>> a = [1, 2, 1, 2]
>>> a.remove(1)
>>> a
[2, 1, 2]
>>> a.remove(2)
>>> a
[1, 2]

s.reverse()

就地将列表中的元素逆序。当反转大尺寸序列时 reverse() 方法会原地修改该序列以保证空间经济性。 为提醒用户此操作是通过间接影响进行的,它并不会返回反转后的序列。

>>> a = [1,2,3,4]
>>> a
[1, 2, 3, 4]
>>> a.reverse()
>>> a
[4, 3, 2, 1]

后面的章节,将通过分析每个序列的使用与源码,进行解读。序列是 python 中非常重要的数据结构,了解序列的构成对深入学习 python 有十分重要的作用。

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

推荐阅读更多精彩内容

  • http://python.jobbole.com/85231/ 关于专业技能写完项目接着写写一名3年工作经验的J...
    燕京博士阅读 7,543评论 1 118
  • 4.6. Sequence Types — list, tuple, range python拥有三种基本的序列类...
    xpf2000阅读 1,655评论 2 2
  • 写在前面的话 代码中的# > 表示的是输出结果 输入 使用input()函数 用法 注意input函数输出的均是字...
    FlyingLittlePG阅读 2,712评论 0 8
  • 最近在慕课网学习廖雪峰老师的Python进阶课程,做笔记总结一下重点。 基本变量及其类型 变量 在Python中,...
    victorsungo阅读 1,654评论 0 5
  • 01 她和他高二文理分班的时候第一次见面。那时,教室里的桌椅堆在一团,他们到的比较早,动手收拾。他看到她那样一个瘦...
    熙熙有曰阅读 295评论 0 3