容器序列:list、tuple和collections.deque能存放不容类型的数据。
特点:容器序列存放的是他们所包含的任意类型的对象的引用
扁平序列:str、bytes、bytearray、memoryview和array.array只能容纳一种类型
特点:存放的是值而不是引用,扁平序列其实是一段连续的内存空间,更加紧凑。
可变序列:list、bytearray、array.array、collections.deque和memoryview
不可变序列:tuple、str和bytes
***列表推导***的作用是什么呢!(生成列表)
Python 3中 列表推导式不会再有变量泄露的问题
生成器表达式
优点:生成器表达式背后遵守了迭代器协 议,可以逐个地产出元素,而不是先建立一个完整的列表,然后再把这 个列表传递到某个构造函数里。前面那种方式显然能够节省内存。
作用:除了用作不可变的列表,它还可以用于没有字段名的记 录。
元组其实是对数据的记录:元组中的每个元素都存放了记录中一个字段 的数据,外加这个字段的位置。
for 循环可以分别提取元组里的元素,也叫作拆包(unpacking)。拆包让元组可以完美地被当作记录来使用。
在 Python 中,函数用 *args 来获取不确定数量的参数算是一种经典写 法了。
嵌套元祖拆包:
在 Python 3 之前,元组可以作为形参放在函数声明中,例如 def fn(a, (b, c), d):。然而 Python 3 不再支持这种格式
具名元祖
collections.namedtuple是一个工厂函数,它可以用来构建一个带字段名的元祖和一个有名字的类——这个带名字的类对调试程序有很大帮助。
‘’’
用 namedtuple 构建的类的实例所消耗的内存跟元组是一样 的,因为字段名都被存在对应的类里面。这个实例跟普通的对象实 例比起来也要小一些,因为 Python 不会用 dict 来存放这些实 例的属性。
‘’’
‘’’
展示如何用具名元组来记录一个城市信息
1、 创建一个具名元组需要两个参数,一个是类名,另一个是类的各个 字段的名字。后者可以是由数个字符串组成的可迭代对象,或者是由空 格分隔开的字段名组成的字符串
2、 *存放在对应字段里的数据要以一串参数的形式传入到构造函数中 *
3、 你可以通过字段名或者位置来获取一个字段的信息。
‘’’
具名元组的属性和方法
作为不可变列表的元祖
列表元祖方法和属性的对比
像列表(list)、元组(tuple)和字符串(str)这类 序列类型都支持切片操作,但是实际上切片操作比人们所想象的要强大 很多。
在切片和区间操作里不包含区间范围的最后一个元素是 Python 的风格, 这个习惯符合 Python、C 和其他语言里以 0 作为起始下标的传统。
对 seq[start:stop:step] 进行求值的时候,Python 会调用 seq.getitem(slice(start, stop, step))。
如果从 Python 用户 的角度出发,切片还有个两个额外的功能:多维切片和省略表示法 (…)。
多维切片
[] 运算符里还可以使用以逗号分开的多个索引或者是切片,外部库 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 解析器眼 里是一个符号,而实际上它是 Ellipsis 对象的别名,而 Ellipsis 对象 2
又是 ellipsis 类的单一实例。 它可以当作切片规范的一部分,也可 以用在函数的参数清单中,比如 f(a, …, z),或 a[i:…]。在 NumPy 中,… 用作多维数组切片的快捷方式。如果 x 是四维数组,那 么 x[i, …] 就是 x[i, :, :, :] 的缩写。如果想了解更多,请参 见“Tentative NumPy Tutorial”(http://wiki.scipy.org/Tentative_NumPy_Tutorial)。
给切片赋值
如果把切片放在赋值语句的左边,或把它作为 del 操作的对象,我们就 可以对序列进行嫁接、切除或就地修改操作。
➊ 如果赋值的对象是一个切片,那么赋值语句的右侧必须是个可迭代 对象。即便只有单独一个值,也要把它转换成可迭代的序列。
对序列使用+和*
任何一本 Python 入门教材都会介绍 + 和 * 的用法,但是在这些用法的背后还有一些可能被忽视的细节。下面 就来看看这两种操作。
注意:+ 和 * 都遵循这个规律,不修改原有的操作对象,而是构建一个全新的 序列。*
*** 序列的增量赋值***
+= 背后的特殊方法是 iadd (用于“就地加法”)。但是如果一个类 没有实现这个方法的话,Python 会退一步调用 add 。
a += b
'''
如果 a 实现了 __iadd__ 方法,就会调用这个方法。
同时对可变序列 (例如 list、bytearray 和 array.array)来说,a 会就地改动,就 像调用了 a.extend(b) 一样。
但是如果 a 没有实现 __iadd__ 的话,a += b 这个表达式的效果就变得跟 a = a + b 一样了:首先计算 a + b,得到一个新的对象,然后赋值给 a。
在这个表达式中, 变量名会不会被关联到新的对象,完全取决于这个类型有没有实现 __iadd__ 这个方法。
总体来讲,可变序列一般都实现了 __iadd__ 方法
'''
** *=:**面所说的这些关于 += 的概念也适用于 *=,不同的是,后者相对应的 是 imul。
'''
对不可变序列进行重复拼接操作的话,效率会很低,因为每次都有一个 新对象,而解释器需要把原来对象中的元素先复制到新的对象里,然后 再追加新的元素。
'''
'''
str 是一个例外,因为对字符串做 += 实在是太普遍了,所以 CPython 对它做了优化。为 str 初始化内存的时候,程序会为它留出额外的可扩展空间,因此进行增量操作的时候,并不会涉 及复制原有字符串到新位置这类操作。
'''
bisect模块包含两个主要函数,bisect和insort,两个函数都利用二分查找算法来在有序序列中查找或插入元素。
用bisect来搜索官网学习
bisect(haystack, needle) 在 haystack里搜索 needle的位置,该位置满足的条件是,把 needle 插入这个位置 之后,haystack 还能保持升序。也就是在说这个函数返回的位置前面 的值,都小于或等于 needle 的值。其中 haystack 必须是一个有序的 序列。你可以先用 bisect(haystack, needle) 查找位置 index,再 用 haystack.insert(index, needle) 来插入新值。但你也可用 insort 来一步到位,并且后者的速度更快一些。
用bisect.insrot插入新元素
排序很耗时,因此在得到一个有序序列之后,我们最好能够保持它的有 序。bisect.insort 就是为了这个而存在的。
insort(seq, item) 把变量 item 插入到序列 seq 中,并能保持 seq 的升序顺序。
要存放 1000 万个浮点数的话,数组(array)的效率要高 得多,因为数组在背后存的并不是 float 对象,而是数字的机器翻 译,也就是字节表述。
如 果需要频繁对序列做先进先出的操作,deque(双端队列)的速度应该 会更快。
数组:
数组支持所有跟可变序列有关的操作,包括.pop、.insert和.extend。另外,数组还提供从文件读取和存入文件的更快方法,如.frombytes和.tofile
创建数组需要一个类型码,这个类 型码用来表示在底层的 C 语言应该存放怎样的数据类型。比如 b 类型码 代表的是有符号的字符(signed char),因此 array(‘b’) 创建出的 数组就只能存放一个字节大小的整数,范围从 -128 到 127,这样在序列 很大的时候,我们能节省很多空间。而且 Python 不会允许你在数组里存 放除指定类型之外的数据。
存入文件和从文件读取
从上面的代码我们能得出结论,array.tofile 和 array.fromfile 用 起来很简单。把这段代码跑一跑,你还会发现它的速度也很快。一个小 试验告诉我,用 array.fromfile 从一个二进制文件里读出 1000 万个 双精度浮点数只需要 0.1 秒,这比从文本文件里读取的速度要快 60 倍,因为后者会使用内置的 float 方法把每一行文字转换成浮点数。
另外,使用 array.tofile 写入到二进制文件,比以每行一个浮点数的 方式把所有数字写入到文本文件要快 7 倍。另外,1000 万个这样的数 在二进制文件里只占用 80 000 000 个字节(每个浮点数占用 8 个字节, 不需要任何额外空间),如果是文本文件的话,我们需要 181 515 739 个字节。
memoryview 是一个内置类,它能让用户在不复制内容的情况下操作同 一个数组的不同切片。
memoryview.cast 会把同一块内存里的内容打包成一个全新的 memoryview 对象给你。