python的序列类型及其特点_Fluent Python 笔记——序列类型及其丰富的操作

序列的分类

Python 标准库用 C 语言实现了丰富的序列类型的数据结构,如:

容器序列(能存放不同类型的数据):list、tuple、collections.deque 等

扁平序列(只容纳同一类型的数据):str、bytes、bytearray、memoryview、array.array

>>> a_list = [1, '2', True, [1, 2, 3], 4.5]

>>> a_str = 'helloworld'

容器序列存放的是对象的引用,扁平序列存放的是值。即扁平序列是一段连续的内存空间。

>>> a_list = [1, '2', True, [1, 2, 3], 4.5]

>>> embedded_list = a_list[3]

>>> embedded_list

[1, 2, 3]

>>> embedded_list.append(4)

>>> embedded_list

[1, 2, 3, 4]

>>> a_list

[1, '2', True, [1, 2, 3, 4], 4.5]

序列还可以按照是否可变(能够被修改)进行分类:

可变序列:list、bytearray、array.array、collections.deque、memoryview

不可变序列:tuple、str、bytes

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

>>> a_list[0] = 2

>>> a_list

[2, 2, 3]

>>> a_tuple = (1, 2, 3)

>>> a_tuple[0] = 2

Traceback (most recent call last):

File "", line 1, in

TypeError: 'tuple' object does not support item assignment

列表推导

for 循环:

>>> symbols = '!@#$%'

>>> codes = []

>>> for symbol in symbols:

... codes.append(ord(symbol))

...

>>> codes

[33, 64, 35, 36, 37]

列表推导:

>>> symbols = '!@#$%'

>>> codes = [ord(symbol) for symbol in symbols]

>>> codes

[33, 64, 35, 36, 37]

通常的原则是,只用列表推导创建新的列表,并尽量保持简短。

列表推导(包括集合推导、字典推导)、生成器表达式在 Python3 中有自己的局部作用域。

>>> x = 'ABC'

>>> dummy = [ord(x) for x in x]

>>> x

'ABC'

>>> dummy

[65, 66, 67]

列表推导与 filter/map 的比较:

>>> symbols = '$¢£¥€¤'

>>> beyond_ascii = [ord(s) for s in symbols if ord(s) > 127]

>>> beyond_ascii

[162, 163, 165, 8364, 164]

>>> beyond_ascii = list(filter(lambda c: c > 127, map(ord, symbols)))

>>> beyond_ascii

[162, 163, 165, 8364, 164]

作为记录的元组

元组其实是一种数据记录(Record),其中的每个元素都对应记录中一个字段的数据,字段在元组中的位置则可以用来区分其含义。

>>> lax_coordinates = (33.9425, -118.408056)

>>> city, year, pop, area = ('Tokyo', 2003, 32450, 8014)

>>> traveler_ids = [('USA', '31195855'), ('BRA', 'CE342567'), ('ESP', 'XDA205856')]

>>> for country, _ in traveler_ids:

... print(country)

...

USA

BRA

ESP

元组拆包

元组拆包可以应用到任何可迭代对象上,唯一的要求即可迭代对象中的元素数量与接收这些元素的空档数一致(除非用 * 忽略多余的元素)。

元组拆包(平行赋值):

>>> lax_coordinates = (33.9425, -118.408056)

>>> latitude, longitude = lax_coordinates

>>> latitude

33.9425

>>> longitude

-118.408056

不使用中间变量交换两个变量的值:

>>> a = 1

>>> b = 2

>>> a, b = b, a

>>> a

2

>>> b

1

使用 * 运算符把一个可迭代对象拆开作为函数的参数:

>>> divmod(20, 8)

(2, 4)

>>> t = (20, 8)

>>> divmod(*t)

(2, 4)

元组拆包可以方便一个函数以元组的方式返回多个值,调用函数的代码就可以轻松地(有选择地)接受这些值。

>>> import os

>>> _, filename = os.path.split('/home/luciano/.ssh/idrsa.pub')

>>> filename

'idrsa.pub'

用 * 处理多余的元素:

>>> a, b, *rest = range(5)

>>> a, b, rest

(0, 1, [2, 3, 4])

>>> a, b, *rest = range(3)

>>> a, b, rest

(0, 1, [2])

>>> a, b, *rest = range(2)

>>> a, b, rest

(0, 1, [])

>>> a, *body, c, d = range(5)

>>> a, body, c, d

(0, [1, 2], 3, 4)

具名元组

collections.namedtuple 可以用来创建一个带字段名的元组和一个有名字的类,便于对程序进行调试。其类实例消耗的内存与元组是一样的,跟普通的对象实例相比更小一些(不用 __dict__ 存放实例的属性)。

>>> from collections import namedtuple

>>> City = namedtuple('City', 'name country population coordinates')

>>> tokyo = City('Tokyo', 'JP', 36.933, (35.689722, 139.691667))

>>> tokyo

City(name='Tokyo', country='JP', population=36.933, coordinates=(35.689722, 139.691667))

>>> tokyo.population

36.933

>>> tokyo.coordinates

(35.689722, 139.691667)

>>> tokyo[1]

'JP'

创建具名元组需要传入两个参数,第一个是类名,第二个是类的各个字段的名称。后者可以是多个字符串组成的可迭代对象或由空格分隔开的字段名组成的字符串。

可以通过字段名或位置获取某个字段的信息。

具名元组的 _fields 属性包含由这个类中所有字段名称组成的元组;_asdict() 方法可以把具名元组以 collections.OrderedDict 的形式返回。

切片

关于切片和区间忽略最后一个元素

在切片和区间操作里不包含最后一个元素是 Python 的风格,同时也符合 C 和其他以 0 为起始下标的语言的习惯。

部分原因如下:

当只有最后一个位置信息时,可以快速看出区间里包含多少个元素:range(3) 和 my_list[:3] 都返回 3 个元素

起止位置都可见时,可以快速算出区间的长度(stop - start),如切片 my_list[3:6] 即包含 6 - 3 = 3 个元素

可以利用任意一个下标把序列分割成不重叠的两部分(my_list[:x] 和 my_list[x:])

step

可以用 s[a:b:c] 的形式对 s 在 a 和 b 之间以 c 为间隔取值。c 值还可以为负,表示反向取值。

>>> s = 'bicycle'

>>> s[::3]

'bye'

>>> s[::-1]

'elcycib'

>>> s[::-2]

'eccb'

对 seq[start:stop:step] 求值时,Python 会调用 seq.__getitem__(slice(start, stop, step))。

对切片赋值

如果把切片放在赋值语句左边,或把它作为 del 操作的对象,则可以对切片所属的序列进行拼接、切除或就地修改等操作。

>>> l = list(range(10))

>>> l

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

>>> l[2:5] = [20, 30]

>>> l

[0, 1, 20, 30, 5, 6, 7, 8, 9]

>>> del l[5:7]

>>> l

[0, 1, 20, 30, 5, 8, 9]

>>> l[3::2] = [11, 22]

>>> l

[0, 1, 20, 11, 5, 22, 9]

>>> l[2:5] = [100]

>>> l

[0, 1, 100, 22, 9]

需要注意的是,在对切片进行赋值操作时,赋值语句的右侧必须是个可迭代对象。

对序列使用 + 和 *

Python 程序员一般默认序列都会支持 + 和 * 的拼接操作。在拼接过程中,两个被操作的序列不会发生任何改动,Python 会创建一个新的包含拼接结果的序列。

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

>>> l * 5

[1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3]

>>> 5 * 'abcd'

'abcdabcdabcdabcdabcd'

如果 a * n 语句中序列 a 里的元素是对其他可变对象的引用的话,这个式子的结果可能会出乎意料。比如用 my_list = [[]] * 3 来初始化一个有列表组成的列表,实际上得到的列表里包含的三个元素是三个引用,且这三个引用都指向同一列表。

>>> weird_board = [['-'] * 3] * 3

>>> weird_board

[['-', '-', '-'], ['-', '-', '-'], ['-', '-', '-']]

>>> weird_board[1][2] = 'O'

>>> weird_board

[['-', '-', 'O'], ['-', '-', 'O'], ['-', '-', 'O']]

其错误的本质等同于如下代码:

>>> row = ['-'] * 3

>>> board = []

>>> for i in range(3):

... board.append(row)

...

>>> board[1][2] = 'O'

>>> board

[['-', '-', 'O'], ['-', '-', 'O'], ['-', '-', 'O']]

即追加同一个行对象(row)到游戏币(board)

正确的做法代码如下:

>>> board = [['-'] * 3 for i in range(3)]

>>> board

[['-', '-', '-'], ['-', '-', '-'], ['-', '-', '-']]

>>> board[1][2] = 'O'

>>> board

[['-', '-', '-'], ['-', '-', 'O'], ['-', '-', '-']]

等同于如下代码:

>>> board = []

>>> for i in range(3):

... row = ['-'] * 3

... board.append(row)

...

>>> board

[['-', '-', '-'], ['-', '-', '-'], ['-', '-', '-']]

>>> board[1][2] = 'O'

>>> board

[['-', '-', '-'], ['-', '-', 'O'], ['-', '-', '-']]

即每次迭代中都新建了一个列表,作为新的一行(row)追加到游戏板子(board)

序列的增量赋值

增量赋值运算符 += 和 *= 的行为取决于第一个操作对象。

+= 调用的特殊方法是 __iadd__(自增)。如果某个类没有实现该方法,Python 会退一步调用 __add__。

如 a += b 就会调用 a 中实现的 __iadd__ 方法,同时对于可变序列(如 list、bytearray、array.array),该方法的行为类似于 a.extend(b),在 a 上就地改动。

如 a 没有实现 __iadd__,a += b 的效果就类似于 a = a + b,计算 a + b 得到一个新的对象,再把这个对象赋值给 a。

*= 对应的是 __imul__。

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

>>> id(l)

2888988078920

>>> l *= 2

>>> l

[1, 2, 3, 1, 2, 3]

>>> id(l)

2888988078920

>>> t = (1, 2, 3)

>>> id(t)

2888988799688

>>> t *= 2

>>> id(t)

2888988107592

作为可变对象的列表运用增量乘法后,ID 没变;而作为不可变对象的元组运用增量乘法后,新的元组被创建。

因此对于不可变序列做重复拼接操作效率会很低,每次都会有一个新对象。但字符串除外,由于对字符串做 += 等操作太普遍,CPython 专门做了优化。在为字符串初始化内存时,程序会预留额外的可扩展空间。

list.sort 与 sorted

list.sort 方法会就地排序列表,即在原列表的基础上完成排序,不会再另外复制一份。也因此其返回值为 None。

内置的 sorted 函数则会新建一个列表作为返回值。它可以接收任何形式的可迭代对象(包含不可变序列和生成器),最后返回的始终是排序好的列表。

>>> fruits = ['grape', 'raspberry', 'apple', 'banana']

>>> sorted(fruits)

['apple', 'banana', 'grape', 'raspberry']

>>> fruits

['grape', 'raspberry', 'apple', 'banana']

>>> sorted(fruits, key=len)

['grape', 'apple', 'banana', 'raspberry']

>>> fruits

['grape', 'raspberry', 'apple', 'banana']

>>> fruits.sort()

>>> fruits

['apple', 'banana', 'grape', 'raspberry']

参考资料

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/476279.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

论文阅读翻译(全)【一直更新】

学习内容 这里面包含了阅读中翻译的概述! CNN is not invariant to scaling and rotation; invariant: 不变的; not invariant: 不是不变的 CNN是变化的! 不和不消除! 1. 词语 1.1 词形 泛…

精选15个国外CSS框架

下面一起来了解一下各种不同的CSS框架吧: 1.960 Grid System 960网格系统是一个通过提供通常使用的尺寸简化网站开发流程的努力的结果,基于960像素的页面宽度。它有两种类型,12和16列,他们可以独立使用或是协同使用。 2.WYMstyle CSS Framewo…

LeetCode 682. 棒球比赛

1. 题目 你现在是棒球比赛记录员。 给定一个字符串列表,每个字符串可以是以下四种类型之一: 1.整数(一轮的得分):直接表示您在本轮中获得的积分数。 2. ""(一轮的得分):…

Lighttpd 的安装配置(web服务器软件)

Lighttpd 的安装配置2006-07-16 01:06lighttpd(http://lighttpd.net/)和apache一样是开源的,与apache相比,虽然功能不及apache完善,稳定性也不如apache,但是,不管是服务静态页面,还是服务动态内容(CGI&…

苹果企业证书_IOS福利9月15日苹果企业证书已更新!重新下载即可!

(视频素材来源于网上)支持系统:苹果上一期你可能错过的:全网短视频免费去水印,还支持解析VIP影视!最近众号修改推送的机制,请大家一定要把公众号:玩转网络科技,设置成“星标⭐”,再点…

LeetCode 697. 数组的度

1. 题目 给定一个非空且只包含非负数的整数数组 nums 数组的度的定义是指数组里任一元素出现频数的最大值 你的任务是找到与 nums 拥有相同大小的度的最短连续子数组,返回其长度。 示例 1: 输入: [1, 2, 2, 3, 1] 输出: 2 解释: 输入数组的度是2,因为…

李宏毅机器学习(四)Spatial Transformer Layer

学习内容 架构; 目的就是克服CNN的旋转和缩放不变性的缺点的! 前情提要 CNN is not invariant to scaling and rotation; CNN对缩放和旋转不是不变的;也就是说CNN是变化的,不具有旋转和缩放不变性; 因为…

我的大理行

离开北京的时候是周五,天气很诡异:潮湿而又充满尘燥,阴霾而又阳光明亮, 真不知道这样的天气怎么能够共存?! 我发神经的做上了飞往云南的航班,这是休息,是治疗。 飞机正位&#xff0c…

LeetCode 703. 数据流中的第K大元素(优先队列)

1. 题目 设计一个找到数据流中第K大元素的类(class)。 注意是排序后的第K大元素,不是第K个不同的元素。 你的 KthLargest 类需要一个同时接收整数 k 和整数数组nums 的构造器,它包含数据流中的初始元素。 每次调用 KthLargest.a…

李宏毅机器学习(五)Transformer

学习内容 前情提要 Sequence-to-sequence(Seq2Seq) 输入是一个句子,输出长度不定; 下面的第一个是语音辨识,第二个是机器翻译,第三个是语音翻译。这三个都是独立的任务。 第三个是语音翻译,就…

Asp.net中的时区

首先&#xff0c;日期时间转换在 Asp.net 中的几种应用&#xff1a; 1. <ASP:BOUNDCOLUMN DATAFIELD "JoinTime " DATAFORMATSTRING "{0:yyyy-MM-dd} " > <ITEMSTYLE WIDTH "18% " > </ITEMSTYLE > </ASP:BOUNDCO…

中文python笔记_Python学习笔记-基础篇

安装LinuxLinux已经自带Python了&#xff0c;我的Centos7自带Python2.7.4&#xff0c;我通过源码安装来更新版本。#!/bin/bash#源码安装wget https://www.python.org/ftp/python/2.7.9/Python-2.7.9.tgztar -zxvf Python-2.7.9.tgzcd Python-2.7.9#编译安装./configuremakemake…

python模块time_Python模块:time模块详解(转)

在平常的代码中&#xff0c;我们常常需要与时间打交道。在Python中&#xff0c;与时间处理有关的模块就包括&#xff1a;time&#xff0c;datetime以及calendar。这篇文章&#xff0c;主要讲解time模块。在开始之前&#xff0c;首先要说明这几点&#xff1a;在Python中&#xf…

李宏毅机器学习(六)自监督学习(一)

学习内容 前情提要 BERT与芝麻街 这些都是BERT的组件 BERT与进击的巨人 BERT&#xff08;340 M 参数&#xff09; ELMO&#xff08;94M&#xff09; GPT-2&#xff08;1542M&#xff09; Megatron&#xff08;8B&#xff09; T5&#xff08;11B&#xff09; Turing NLG&…

LeetCode 717. 1比特与2比特字符

1. 题目 有两种特殊字符。第一种字符可以用一比特0来表示。第二种字符可以用两比特(10 或 11)来表示。 现给一个由若干比特组成的字符串。 问最后一个字符是否必定为一个一比特字符。 给定的字符串总是由0结束。 示例 1: 输入: bits [1, 0, 0] 输出: True 解释: 唯一的编…

制作多系统启动盘教程_制作U盘启动盘教程

制作 U 盘启动盘图文教程 在笔记本电脑早已普及到会议室的这个年代&#xff0c;商务人士拿笔记本来演示 PPT 以及做电子版 的会议记录&#xff1b;在笔记本电脑已经普及到教室的这个年代&#xff0c;学生们甚至在用笔记本翻阅资料 进行开卷考试。随着笔记本电脑正在成为人们生活…

移动设备应用程序开发入门一:创建用于设备的 Windows 窗体应用程序并打包进行部署...

目标&#xff1a;了解如何使用 Visual C# 创建一个简单的 Hello World 应用程序&#xff0c;该应用程序可在 Pocket PC 上运行。 了解如何为应用程序创建随后可部署到智能设备上的 .cab 文件。 创建使用 Windows 窗体的设备项目 (Visual C#) 在 Visual Studio 中的“文件”菜单…

论文阅读笔记(五)【ACL 2021】Answering Ambiguous Questions through Generative Evidence Fusion and Round-Trip P

通过生成性证据融合和往返预测回答模糊问题 关键词&#xff1a; 生成性证据融合&#xff1b; 往返预测&#xff1b; 模糊问题 摘要 一般的开放域QA问题&#xff1a; 在开放域问答中&#xff0c;问题很可能是模棱两可的&#xff0c;因为用户在制定问题时可能不知道相关主题的…

c语言不会可以学好java吗_不会C语言能学Java吗

不会C语言能学Java吗&#xff1f;当然可以&#xff0c;学码思不少小伙伴没有任何代码基础&#xff0c;来这里Java培训学的也非常不错&#xff0c;都获得了理想的就业。现在时代不一样了&#xff0c;编程语言更新换代的速度也很快&#xff0c;很多老程序员都是从C语言入门&#…