第二章(提炼) 序列构成的数组(二)

元组除了用作“不可变的列表”,还可以用于记录没有字段名的记录。

一. 元组不仅仅是不可变列表

1.1 元组和记录

        元组其实是对数据的记录:元组中的每个元素都存放了记录中的一个字段,外加这个字段的位置。正是这个位置信息给数据赋予了意义。如果只把元组理解为不可变列表,那其它信息——它所含有的元素的总数和它们的位置,似乎就变得可有可无。但是如果把元组当作一些字段的集合,那么数量和位置信息就变得非常重要了。

演示1  把元组当作记录

1.2 元组拆包

        元组拆包可以应用到任何可迭代对象上,唯一硬性的要求是,被迭代对象中的元素数量必须要跟接受这些元素的元组的空档数一致。除非使用*来表示忽略多余的元素。最好辨认的元组拆包形式就是平行赋值,也就是说把一个可迭代对象里的元素,一并赋值到由对应的变量组成的元组中。

另外一个很优雅的写法,当属不使用中间变量交换2个变量的值:

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

下面的例子中,元组拆包的用法则是让一个函数可以用元组的形式返回多个值,然后调用函数的代码就能轻松地接受这些返回值。os.path.split()函数就会返回以路径和最后一个文件名组成的元组:

在python中函数用*args来接收不确定数量的参数算是一种经典的写法了。于是Python3里,这个概念被扩展到了平行赋值中:

在平行赋值中,*前缀只能用在一个变量名的前面,但是这个变量可以出现在赋值表达式的任意位置:

1.3 嵌套元组拆包

        嵌套元组拆包是指接受表达式的元组可以是嵌套形式的。

演示2  利用嵌套元组来获取经纬度

        元组已经设计的很好了,但作为记录来用的话,还是少了一个功能:我们时长会需要给记录中的字段命名。 namedtuple函数的出现帮我们解决了这个问题。

1.4 具名元组

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

演示3  定义和使用具名元组

        创建一个具名元组需要2个参数,一个是类名,另一个是类的各个字段的名称。后者可以是由数个字符串组成的可迭代对象,或者是由空格分隔的字段组成的字符串。 存放在对应字段里面的数据要以一串参数的形式传入构造函数中。具名元组允许通过字段名或位置信息来获取一个字段的信息。

        除了从普通元组那里继承来的属性外,具名元组还有一些自己专有的属性。如:_fields类属性、实例方法_asdict()。

演示4  具名元组的属性和方法

        _fields属性是一个包含这个类所有字段名称的元组。用_asdict()把具名元组以collections.OrderedDict的形式返回,我们可以利用它来把元组里的信息友好地呈现出来。现在我们已经知道了,元组是一种强大的可以当作记录来用的数据类型。它的第二个角色是充当一个不可变的列表。

1.5 作为不可变列表的元组

        除了跟增减元素相关的方法之外,元组支持列表的其它所有方法。还有一个例外,元组没有__reversed__方法,但是这个方法只是个优化而已,reversed(obj)这个方法在没有__reversed__的情况下也是合法的。

二. 切片

        在Python里,像列表、元组和字符串这类序列类型都支持切片操作,但是实际上切片操作比人们所想象的要强大得多。

2.1 为什么切片和区间会忽略最后一个元素

        在切片和区间操作里不包含区间范围的最后一个元素是Python的风格:

  1. 当只有最后一个位置信息时,我们也可以快速看出切片和区间里有几个元素:range(3)和list[:3]都返回3个元素;
  2. 当起止位置信息都可见时,我们可以快速计算出切片和区间的长度:stop-start;
  3. 这样作也让我们可以利用任意一个下标来把序列分割成不重叠的2部分,只要写成my_list[:x]和my_list[x:]就可以了。

2.2 对对象进行切片

演示5  使用s[a:b:c]的形式对s在a和b之间以c为间隔取值。c的值可以为负,负意味着反向取值:

        a:b:c这种用法只能作为索引或者下标作用在[]中来返回一个切片对象:slice(a, b, c)。对seq[start:stop:step]进行求值的时候,Python会调用seq.__getitem__(slice(a, b, c))。(注:原书的第十章有对切片对象的详细介绍。)

演示6 将纯文本形式的收据以一行字符串的形式被解析

2.3 多维切片和省略

        []运算符还可以使用以逗号分开的多个索引或是切片,外部库numpy就用到了这个特性,二维的numpy.ndarray就可以用a[i, j]的形式获取值,抑或是用a[m:n, k:l]的方式来得到二维切片。要正确处理这种运算符的话,对象的特殊方法__getitem__和__setitem__需要以元组的形式来接收a[i, j]中的索引。即:如果要得到a[i, j]的值,Python会调用a.__getitem__((i, j))。Python内置的序列都是一维的,因此只支持单一索引。

        省略的具体写法是三个英语句号(...),省略在Python解释器眼里是一个符号,而实际上它是Ellipsis对象的别名,而Ellipsis又是ellipsis类的单一实例(你没看错,ellipsis是类名,全小写,而它的内置实例写作Ellipsis。这跟bool是小写,但是它的两个实例写作True/False是异曲同工)。它可以当作切片规范的一部分,也可以用在函数的参数清单中,比如f(a, ..., z)或a[i:...]。在numpy中,...用作多维数组切片的快捷方式。如x是四位数组,那么x[i, ...]就是x[i, ;, ;, ;]的缩写。

        Ellipsis或是多维索引的句法,只要是为了支持用户自定义类或者扩展,比如Numpy就是例子。切片除了用来提取序列里的内容,还可以用来就地修改可变序列。

2.4 给切片赋值

演示7  如果把切片放在赋值语句的左边,或把它作为del操作的对象,我们就可以对序列进行嫁接、切除或就地修改操作。

        如果赋值的对象是一个切片,那么赋值语句的右侧必须是一个可迭代对象。即便只有单独一个值,也要把它转换成可迭代的序列。

发布了132 篇原创文章 · 获赞 14 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/Geroge_lmx/article/details/105294995
今日推荐