这一篇是《流畅的 python》读书笔记。主要介绍元组、分片、序列赋值以及引用了大师 Edsger W.Dijkstra为什么序列从0开始计数的解释。

元组

在有些python 的介绍中,元组被称为不可变列表,这其实是不准确的,没有完全概括元组的特点。元组除了用作不可变列表,还可以用于没有字段名的记录

元组和记录

元组其实是对数据的记录:元组中的每个元素都存放了记录中一个字段的数据,外加这个数据的位置。

如果把元组当作一些字段的集合,数量和位置信息会变得非常重要。比如以下几条用元组表示的记录:

 >>> lax_coordinates = (33.9425, -118.408056) # 洛杉矶国际机场的经纬度# 东京的一些信息:市名、年份、人口、人口变化和面积>>> city, year, pop, chg, area = ('Tokyo', 2003, 32450, 0.66, 8014)复制代码

以上这两个元组每个位置都对应一个数据记录。

元组拆包

>>> city, year, pop, chg, area = ('Tokyo', 2003, 32450, 0.66, 8014)复制代码

这个例子中,我们把元组的数据用一条语句分别赋值给 city, year, pop, chg, area,这就是元组拆包的一个具体应用。

元组拆包可以应用到任何可迭代对象上,但是被迭代的对象窄的元素的数量必须跟接受这些元素的元组的空档数一致。

比如:

>>> lax_coordinates = (33.9425, -118.408056)
>>> latitude, longitude = lax_coordinates
>>> latitude
33.9425
>>> longitude
-118.408056复制代码

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

>>> divmod(20, 8)
(2, 4)
>>> t = (20, 8)
>>> divmode(*t)
(2, 4)
>>> quotient, remainder = divmode(*t)
>>> quotient, remainder
(2, 4)复制代码

在进行拆包是,我们可能对元组的某些值并不感兴趣,这时可以用 _ 占位符处理。比如:

>>> divmode(20, 8)
(2, 4)
>>> _, remainder = divmode(20, 8)  # 这里我们只关心第二个值
>>> remainder
4复制代码

在处理函数参数时,我们经常用*args 来表示不确定数量的参数。在python3中,这个概念被扩展到了平行赋值中:

# python 3 代码示例
>>> a, b, *rest = range(5)
>> a, b, rest
(0, 1, [2, 3, 4])
# * 前缀只能用在一个变量名前,这个变量可以在其他位置
>>> a, *rest, c, d = range(5)
>> a, rest, c, d
(0, [1, 2], 3, 4)
>>> a, b, *rest = range(2)
>> a, b, rest
(0, 1, [])复制代码

元组也支持嵌套拆包,比如:

>>> l = (1, 2, 3, (4, 5))
>>> a, b, c, (d, e) = l
>>> d
4
>>> 5
4复制代码

具名元组

元组作为记录除了位置以外还少一个功能,那就是无法给字段命名,namedtuple解决了这个问题。

namedtuple 使用方式实例:

>>> from collecitons import namedtuple
>>> city = namedtuple('City', 'name country population coordinates')
>>> tokyo = City('Tokyo', 'JP', 36.933, (35.689722, 139.691667))
>>> tokyo.population  # 可以使用字段名获取字段信息
36.933
>>> tokyo[1] # 也可以使用位置获取字段信息
'JP'
>>> City._fields # _fields 属性是一个包含这个类所有字段名的元组
('name', 'country', 'population', 'coordinates')
>>> tokyo_data = ('Tokyo', 'JP', 36.933, (35.689722, 139.691667))
>>> tokyo = City._make(tokyo_data) # _make() 方法接受一个可迭代对象生成这个类的实例,和 City(*tokyo_data) 作用一致
>>>  tokyo._asdict() # _asdict() 把具名元组以 collections.OrderedDict 的形式呈现
OrderedDict([('name', 'Tokyo'), ('country', 'JP'), ('population', 36.933), ('coordinates', (35.689722, 139.691667))])复制代码

collections.namedtuple 是一个工厂函数,它可以用来构建一个带字段名的元组和一个有名字的类。
namedtuple 构建的类的实例锁消耗的内存和元组是一样的,因为字段名都被存放在对应的类里。这个实例和普通的对象实例相比也更小一些,因为 在这个实例中,Python 不需要用 __dict__ 来存放这些实例的属性

切片

Python 中列表、元组、字符串都支持切片操作。

在切片和区间操作里不包含区间范围的最后一个元素是 Python 的风格。这样做的好处如下:

  • 当只有最后一个位置信息时,我们可以快速看出切片和区间里有几个元素:range(3) 和 mylist[:3] 都只返回三个元素
  • 当气质位置可见时,可以快速计算出切片和区间的长度,用后一个数减去第一个下标(stop-start)即可。
  • 这样还可以让我们利用任意一个下标来把序列分割成不重复的两部分,只要写成 mylist[:x] 和 mylist[x:] 就可以。

切片除了开始和结束的下标之外还可以有第三个参数,比如:s[a:b:c],这里 c 表示取值的间隔,c 还可以为负值,负值意味着反向取值。

>>> s = 'bicycle'
>>> s[::3]
'bye'
>>> s[::-1]
'elcycib'
>>> s[::2]
'eccb'复制代码

a:b:c 这种用法只能作为索引或者下标在[] 中返回一个切片对象:slice(a, b, c)。对 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]
[0, 1, 20, 30, 5, 8, 9]
>>> l[3::2] = [11, 22]
>>> l
[0, 1, 20, 11, 5, 22, 9]
>>> l[2:5] = 100
Traceback (most recent call last):file "<stdin>", line 1 in <moduld>
TypeError: can only assign an iterable复制代码

如果赋值的对象是一个切片,那么赋值语句的右侧必须是一个可迭代对象。

给切片命名

如果代码中已经出现了大量的无法直视的硬编码切片下标,可以使用给切片命名的方式清理代码。比如你有一段代码要从一个记录字符串中几个固定位置提取出特定的数据字段 比如文件或类似格式 :

### 01234567890123456789012345678901234567890123456789012345678901234
record = '............100....513.25........'
cost = int(record[20:23]) * float(record[31:37])
# 这时,可以先给切片命名,以避免大量无法理解的硬编码下标,使代码可读性更强
SHARES= slice(20, 23)
PRICE = slice(31, 37)
cost = int(record[SHARES]) * float(record[PRICE])复制代码

slice() 函数创建了一个切片对象,可以被用在任何切片允许使用的地方,比如:

>>> items = [0, 1, 2, 3, 4, 5, 6]
>>> a = slice(2, 4)
>>> items[2:4]
[2, 3]
>>> items[a]
[2, 3]
>>> items[a] = [10, 11]
>>> items
[0, 1, 10, 11, 4, 5, 6]复制代码

如果你有一个切片对象 a,还可以调用 a.start, a.stop, a.step 来获取更多信息,比如:

>>> a = slice(5, 50, 2)
>>> a.start
5
>>> a.step
2复制代码

扩展阅读 为什么下标要从0开始

Python 里的范围(range)和切片都不会返回第二个下标所指的元素,计算机科学领域的大师 Edsger W.Dijkstra 在一个很短的备忘录 Why numbering should start at zero 里对这一惯例做了说明。以下是部分关键说明:

为了表示出自然数的子序列,2, 3, ... , 12,不使用省略记号那三个点号,我们可以选择4种约定方式:

  • a) 2 ≤ i < 13
  • b) 1 < i ≤ 12
  • c) 2 ≤ i ≤ 12
  • d) 1 < i < 13

是否有什么理由,使选择其中一种约定比其它约定要好呢?是的,确实有理由。可以观察到,a) 和 b)有个优点,上下边界的相减得到的差,正好等于子序列的长度。另外,作为推论,下面观察也成立:在 a),b)中,假如两个子序列相邻的话,其中一个序列的上界,就等于另一个序列的下界。但上面观察,并不能让我们从a), b)两者中选出更好的一个。让我们重新开始分析。

一定存在最小的自然数。假如像b)和d)那样,子序列并不包括下界,那么当子序列从最小的自然数开始算起的时候,会使得下界进入非自然数的区域。这就比较丑陋了。所以对于下界来说,我们更应该采用≤,正如a)或c)那样。
现在考虑,假如子序列包括上界,那么当子序列从最小的自然数开始算起,并且序列为空的时候,上界也会进入非自然数的区域。这也是丑陋的。所以,对于上界,我们更应该采用 <, 正如a)或b)那样。因此我们得出结论,约定a)是更好的选择。

  • 比如要表示 0, 1, 2, 3 如果用 b) d) 的方式,下界就要表示成 -1 < i
  • 如果一个空序列用 c) 其实是无法表示的,用 a) 则可以表示成 0 ≤ i < 0

总结

这一篇主要介绍元组、分片、序列赋值以及对为什么序列从0开始计数做了摘录。

参考链接

  • Why numbering should start at zero
  • Why numbering should start at zero: http://www.cs.utexas.edu/users/EWD/ewd08xx/EWD831.PDF

最后,感谢女朋友支持。

欢迎关注(April_Louisa) 请我喝芬达
欢迎关注
请我喝芬达

为什么程序要从0开始计数相关推荐

  1. Java黑皮书课后题第7章:*7.7(统计个位数的数目)编写一个程序,生成0和9之间的100个随机整数,然后显示每一个数出现的次数

    *7.7(统计个位数的数目)编写一个程序,生成0和9之间的100个随机整数,然后显示每一个数出现的次数 题目 题目描述 破题 代码 运行示例 题目 题目描述 *7.7(统计个位数的数目)编写一个程序, ...

  2. arraylist下标从几开始_漫画:为什么计算机从 0 开始计数,而不是从 1 开始?

    作者 | 漫话编程 来源 | 漫话编程 当我们想要写一个循环体,期望执行10次的时候,我们会使用以下方式: for (int i=0; i<10; i++){ } 可以看到,为了保证循环10次, ...

  3. python中的索引从几开始计数_计算机为什么要从 0 开始计数?

    作者:程序喵大人 来源:程序喵大人 大家好,我是猫哥! 众所周知,计算机是从0开始计数,而不是我们平时常用的从1开始计数,但你有想过为什么吗? 其实不是计算机从0开始计数而是多数编程语言中的数组都使用 ...

  4. 程序员新手 0年份等级 指导(一) 开发人员IT架构总览

    程序员新手 0年份等级 指导(一) 开发人员IT架构总览 程序员新手 0年份等级 指导(一) 开发人员相关IT架构总览之职能分解 开发人员IT架构总览 一.职能分解 软件项目的主要组成大体上按照一个项 ...

  5. java数据库防火墙,数据库centos7防火墙导致java程序访问mongodb3.0.1时报错的问题分析...

    环境描述: 数据库:mongodb3.0.1 数据库系统:centos7,(虚拟机,最小安装) 数据库驱动:mongo-Java-driver-3.0.0.jar 问题描述:shell环境下用mong ...

  6. 微信(支付宝)小程序蓝牙4.0线上项目

    需求 : 微信(支付宝)小程序链接BLE4.0 ,发送指令到蓝牙硬件 过程 : 小程序分为安卓和ios两套系统,支持连接BLE 蓝牙 ,其中会遇到机型问题(其中安卓,华为荣耀机型,小米,问题很多,稍微 ...

  7. 定时器中断实验 编写程序使定时器0或者定时器1工作在方式2,自动重装载模式,定时500ms使两位数码管从00、01、02……98、99每间隔500ms加1显示。

    编写程序使定时器0或者定时器1工作在方式2,自动重装载模式,定时500ms使两位数码管从00.01.02--98.99每间隔500ms加1显示. 程序: #include <reg51.h> ...

  8. 定时器中断实验 编写程序使定时器0或者定时器1工作在方式1,定时500ms使两位数码管从00、01、02……98、99每间隔500ms加1显示。

    编写程序使定时器0或者定时器1工作在方式1,定时500ms使两位数码管从00.01.02--98.99每间隔500ms加1显示. 程序: #include <reg51.h> #defin ...

  9. 定时器中断实验 编写程序使定时器0或者定时器1工作在方式1,定时50ms触发蜂鸣器。

    编写程序使定时器0或者定时器1工作在方式1,定时50ms触发蜂鸣器. 程序: #include<reg51.h> sbit fm=P2^3; unsigned char cnt; int ...

最新文章

  1. 机器学习笔试题精选(五)
  2. Android应用中如何保护JAVA代码
  3. 盘神 Pandownload 复活
  4. B树、B+树、AVL树、红黑树
  5. PostgreSQL、Greenplum 日常监控 和 维护任务
  6. Linux三大主流网站构建平台,Linux快速构建LAMP网站平台
  7. 家用电器如何计算功率和消耗的度数
  8. WARNING:Result from SERVER not valid. Partial Result:
  9. hibernate - Transaction not successfully started
  10. 基于ffmpeg的kxmovie的使用
  11. Flash player 10(FLEX 4)的安全沙箱机制
  12. 嵌入式 Linux 入门 环境篇(四、必备开发工具安装)
  13. html添加B站视频,iframe嵌入BiliBili视频方法B站视频外链
  14. Android Uri的几种使用方法,分享共同学习
  15. 川土微电子携CA-IS3062W 突围高端隔离器模拟芯片市场
  16. Android源码国内源下载
  17. JVM的mixed mode
  18. 安全多方计算之四:比特承诺
  19. AS中码云和GitHub的使用入门
  20. 云计算的概念、发展和原理

热门文章

  1. 【C++】max_element() 和 min_element()
  2. 蓝桥杯 BASIC-10 基础练习 十进制转十六进制
  3. Python获取Redis所有Key以及内容
  4. AngularJs+bootstrap搭载前台框架——基础页面
  5. Endless Spin
  6. 微软加入反 Flash 阵营,新版 Edge 默认屏蔽 Flash
  7. boost::asio::deadline_timer(理解)
  8. NS3 MyApp Class Reference
  9. Unity 5.x---00使用重力
  10. 【iOS】UIViewController、UINavigationController与UITabBarController的整合使用