2020.6.23 第一章:数据结构和算法--python cookbook 3rd
Python提供了数值、字符串、布尔值和空值4种基础数据类型,其中数值包括:整数、浮点数,空值用None表示。
对于复杂的问题,最基础的数据类型可能没法解决,于是引入Python内置的数据结构,包括:列表、元组、字典和集合。大多数情况下使用这些数据结构是很简单的。 但是,经常碰到到诸如增加、删改、查询、排序和过滤等等这些普遍存在的问题。
1.1 将序列分解给单独的变量
问题
现在有一个包含 N 个元素的元组或者是序列,怎样将它里面的值解压后同时赋值给 N 个变量?
解决方案
任何的序列(或者是可迭代对象)可以通过一个简单的赋值操作来分解为单独的变量。 唯一的要求就是变量的总数和结构必须与序列相吻合。
如果元素的数量不匹配,会得到一个错误提示。
凡是直接作用于for循环的对象统称为可迭代对象:Iterable。可以直接作用于for循环的数据类型有以下几种:
一类是集合数据类型,如list、tuple、dict、set、str等;一类是generator,包括生成器和带yield的generator function。
凡是可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator,它们表示一个惰性计算的序列
生成器都是Iterator对象,但list、dict、str虽然是Iterable,却不是Iterator。
把list、dict、str等Iterable变成Iterator可以使用iter()函数。
Python的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。Iterator甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。
不仅仅只是元组或列表,只要对象是可迭代的,就可以执行分解操作。 包括字符串,文件对象,迭代器和生成器。
如果只想解压一部分,丢弃其他的值,可以使用任意变量名去占位,到时候丢掉这些变量就行了:
>>> data = [ 'ACME', 50, 91.1, (2012, 12, 21) ]
>>> _, shares, price, _ = data
1.2 解压可迭代对象赋值给多个变量
问题
如果一个可迭代对象的元素个数超过变量个数时,会抛出一个 ValueError 。 那么怎样才能从这个可迭代对象中解压出 N 个元素出来?
解决方案
星号表达式可以用来解决这个问题。
>>> record = ('Dave', 'dave@example.com', '773-555-1212', '847-555-1212')
>>> name, email, *phone_numbers = record
>>> name
'Dave'
>>> email
'dave@example.com'
>>> phone_numbers
['773-555-1212', '847-555-1212']
值得注意的是上面解压出的 phone_numbers 变量永远都是列表类型,不管解压的电话号码数量是多少(包括 0 个)。 所以,任何使用到 phone_numbers 变量的代码就不需要做多余的类型检查去确认它是否是列表类型了。
扩展的迭代解压语法是专门为解压不确定个数或任意个数元素的可迭代对象而设计的。 通常,这些可迭代对象的元素结构有确定的规则, 星号表达式让开发人员可以很容易的利用这些规则来解压出元素来。
星号表达式在迭代元素为可变长元组的序列时是很有用的。
星号解压语法在字符串操作的时候也会很有用,比如字符串的分割。
想解压一些元素后丢弃它们,可以使用一个普通的废弃名称,比如 _ 或者 ign(ignore)。
在很多函数式语言中,星号解压语法跟列表处理有许多相似之处。比如,如果你有一个列表, 你可以很容易的将它分割成前后两部分。
1.3 保留最后 N 个元素
collections是python的一个内置集合模块,其中提供的deque(double-ended queue)可以创建一个双向的list,这个双向的list既可以从左边操作,又可以从右边操作。deque(maxlen=N)可以限定该双向list的元素为N个,当有新的元素加入的时候,最老的元素会被移除(如果满了N个的话),这样也就保留了最后N个元素。在队列两端插入或删除元素时间复杂度都是 O(1) ,区别于列表,在列表的开头插入或删除元素的时间复杂度为 O(N) 。
1.4 查找最大或最小的 N 个元素
heapq 模块有两个函数:nlargest() 和 nsmallest() 可以完美解决这个问题。两个函数都能接受一个关键字参数,用于更复杂的数据结构中。
当要查找的元素个数相对比较小的时候,函数 nlargest() 和 nsmallest() 是很合适的。 如果你仅仅想查找唯一的最小或最大(N=1)的元素的话,那么使用 min() 和 max() 函数会更快些。 类似的,如果 N 的大小和集合大小接近的时候,通常先排序这个集合然后再使用切片操作会更快点 ( sorted(items)[:N] 或者是 sorted(items)[-N:] )。 需要在正确场合使用函数 nlargest() 和 nsmallest() 才能发挥它们的优势 (如果 N 快接近集合大小了,那么使用排序操作会更好些)。