1、内置序列类型

容器序列:list,tuple,collections.deque  (能存放不同类型)

扁平序列:str,bytes,bytearray,memoryview,array.array

▲ 容器序列存放的是所包含的任意类型的对象引用。

可变序列:list,bytearray,memoryview,array.array,collection.deque

不可变序列:str,tuple,bytes

2.1、列表推导和生成器表达式

列表推导(listcomps)是构建列表的快捷方式,通常原则是只用列表推导来创建新的列表。

生成器表达式(genexps)用来创建其他类型的序列。



>>> x = 'ABC'
>>> dummy = [ord(x) for x in x]
>>> x
'ABC'
>>> dummy
[65,66,67]



▲ Python3 中的列表推导式、生成器表达式、集合推导、字典推导都有自己的局部作用域。表达式内部的变量和赋值只在局部作用域生效。

列表推导可以帮助我们把一个序列或是其他可迭代类型中的元素进行过滤或是加工,然后再新建一个列表。

2.2、列表推导同 filtermap 比较



>>> x = 'ABCDEF'
>>> dummy = [ord(x) for x in x if ord(x)>66]
>>> dummy
[66,67,68,69,70]
>>> dummy = list( filter(lambda c:c>66,map(ord,x)) )
>>> dummy
[66,67,68,69,70]



2.3、生成器表达式

生成器表达式遵守迭代器协议,可以逐个产出元素,而不是先建立一个完整的列表。

▲ 生成器表达式的语法和列表推导差不多,只是把方括号改成圆括号!



colors  = ['black','white']
sizes = ['S','M','L']
for tshirt in ('%s %s'%(c,s) for c in colors for s in sizes):
    print(tshirt)



3.1、元组的拆包

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

元组的拆包形式之一(平行赋值)



>>> scores = (93,97)
>>> CN,EN = scores   #元组拆包
>>> CN
93
>>> EN
97



不使用中间变量交换元素



>>> a,b = b,a



用 * 运算符把 可迭代对象



>>> t = (20,8)
>>> divmod(*t)
(2,4)



让函数用元组的形式返回多个值



>>> import os 
>>> _,filename = os.path.split('/home/abc.py')
>>> filename
abc.py



_ 占位符能帮助处理不感兴趣的数据。

用 * 号处理剩下的元素(这个变量可以放在赋值表达式的任意位置)



>>> a,b,*rest = range(5)
>>> a,b,rest
(0,1,[2,3,4])

>>> a,*body,b,c = range(5)
>>> a,*body,b,c
(0,[1,2],3,4)



嵌套元组的拆包



class1 = [
    ('xiaoming','Boy',(85,88,90)),
    ('xiaohong','Girl',(90,88,90)),
]
for name,sex,scores in class1:
    print(name,sex,scores)



3.2、具名元组

collections.namedtuple 是一个工厂函数,用来创建一个带字段名的元组和一个有名字的类。

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



>>> from collections import namedtuple
>>> City = namedtuple('City','name country population coordinates')    # <class '__main__.City'>
>>> tokyo = City('Tokyo','JP',38.32,(35.68872,139.25436))
>>> tokyo
City(name='Tokyo', country='JP', population=38.32, coordinates=(35.68872, 139.25436))
>>> tokyo.population
38.32
>>> tokyo[0]
'Tokyo'



具名元组还具有自己专有属性:_fields类属性,类方法_make(iterable),实例方法_asdict()



>>> City._fields
('name', 'country', 'population', 'coordinates')

>>> LatLong = namedtuple('LatLong','lat long')
>>> delhi_data = ('Delhi NCR','IN',32.21,LatLong(28.45721,76.89342))
>>> delhi = City._make(delhi_data)     # 生成实例类似 City(*delhi_data)

>>> delhi._asdict()
OrderedDict([('name', 'Delhi NCR'), ('country', 'IN'), ('population', 32.21), ('coordinates', LatLong(lat=28.45721, long=76.89342))])



4.1、切片

在切片和区间操作里不包含区间范围的最后一个元素。这样带来的好处有:

● 当只有最有一个位置信息时,我们也可以快速看出切片和区间里有几个元素:range(3) 和 my_list[:3] 都返回3个元素。

● 当起止位置星系都可见时,可以计算出切片和区间的长度(stop - start )

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

4.2、对对象进行切片

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



>>> HTML = """ ... 文本内容 """
>>> TEXT_HEAD = slice(0,23)
>>> IMG_URL = slice(23,56)
>>> for item in HTML:
       print(item[TEXT_HEAD],item[IMG_URL])



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



>>> li = list(range(10))
>>> li[2:5] = 100
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'range' object does not support item assignment
>>> li[2:5] = [100]



5、对序列使用 + 和 *

+ 号两侧的序列由同类型的数据所构成,在拼接的过程中,两个被操作的序列都不会被修改。

Python会新建一个包含同类型数据的序列来作为拼接的结果。

▲ 在 a * n 语句中,如果序列a里的元素是对其他可变对象的引用,得到的列表里包含的3个元素其实是3个引用。



>>> my_list = [[]] * 3
>>> my_list
[[], [], []]
>>> print(id(my_list[0]),id(my_list[1]),id(my_list[2]))
1767860956104 1767860956104 1767860956104

>>> my_list = [[''] *3 for i in range(3) ] 
>>> print(id(my_list[0]),id(my_list[1]),id(my_list[2]))
1767860947400 1767860945992 1767860953544



增量赋值运算符 += 和 *= 利用的特殊方法是 __iadd__ (就地加法)和 __imul__,但是如果没有实现  __iadd__ 和 __imul__ 的话,

a += b 效果就变成了 a = a + b,首先计算 a + b,得到一个新的对象,然后赋值给 a。

可变序列一般都实现了 __iadd__ 和 __imul__ 

一个罕见的边界情况:



>>> t = (1,2,[3,4])
>>> t[2] += [5,6]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>> t
(1, 2, [3, 4, 5, 6])



▲ 不要把可变对象放在元组里面。

▲ 增量赋值不是一个原子操作。

▲ 不要把可变对象放在元组里面。

▲ Python 运行原理可视化分析工具 →  http://www.pythontutor.com/visualize.html#mode=edit

6.1、list.sort 方法和内置函数 sorted()

list.sort 方法会就地排序列表,返回None值。

sorted() 方法会新建一个列表作为返回值。

list.sort 和 sorted函数,都有两个可选的关键字参数。

reverse :被设定为 True,被排序的列表的元素会以降序输出。

key:一个只有一个参数的函数,这个函数会被用在序列里的每一个元素上,所产生的结果将是排序算法依赖的对比关键字,如key=len

key参数也可以对一个混有数字字符和数值的列表进行排序。



>>> l = [1,7,3,'2','6',8]
>>> sorted(l,key=int)
>>> sorted(l,key=str)



6.2、用bisect来管理已排序的序列

bisect包含两个主要函数,bisect insort,都利用二分查找算法在有序序列中查找或插入元素。

bisect 的表现可以从两个方面进行:

  1. bisect 有两个可选参数:lo 和 hi 来缩小搜寻范围,lo默认值是0,hi默认值是序列的长度len()。
  2. bisect函数其实是bisect_right函数的别名,还有一个bisect_left函数,区别在于新元素会放置于它相等的元素的前面
def grade(score,breakpoint=[60,70,80,90],grade='FDCBA'):
    i = bisect.bisect(breakpoint,score)
    return grade[i]



7.1、当列表不是首选时

如果我们需要一个只包含数字的列表,那么array.array 比 list 更加高效。

创建数组需要一个类型码,类型码用来表示在底层的C语言应该存放怎样的数据类型。

类型代码

C类型

Python类型

最小大小(以字节为单位)

'b'

signed char

INT

1

'B'

unsigned char

INT

1

'u'

Py_UNICODE

Unicode字符

2

'h'

signed short

INT

2

'H'

unsigned short

INT

2

'i'

签名int

INT

2

'I'

无符号整数

INT

2

'l'

signed long

INT

4

'L'

无符号长整数

INT

4

'q'

signed long long

INT

8

'q'

无符号long long

INT

8

'f'

float

float

4

'd'

double

float

8

数组还提供从文件读取和存入文件的更快的方法。



from array import array
from random import random
floats = array('d',(random() for i in range(10**7)))
print(floats[-1])

fp = open('floats.bin','wb')
floats.tofile(fp)
fp.close()

floats2 = array('d')
fp = open('floats.bin','rb')
floats2.fromfile(fp,10**7)
fp.close()



▲ 数组的排序:a = array.array(a.typecode, sorted(a))

7.2、内存=视图

memoryview 是一个内置类,能在不复制内容的情况下操作同一个数组的不同切片。



a = bytearray('aaaaaaa',encoding='utf8')

b = a[:2]
b[:2] = b'bb'
print(a,b)       
# bytearray(b'aaaaaaa')   bytearray(b'bb')

ma = memoryview(a)
mb = ma[:2]
mb[:2] = b'bb'

print(ma.tobytes(),mb.tobytes())   
# b'bbaaaaa'     b'bb'



使用场景是网络程序中socket接收和接收数据的解析

7.3、双向队列和其他形式的队列

collections.deque 类是一个线程安全,可以快速从两端添加或者删除元素的数据类型。



>>> from collections import deque
>>> dq = deque(range(10),maxlen=10)
>>> dq
deque([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], maxlen=10)
>>> dq.rotate(3)     # 当n>0时,最右边n个元素移到最左边,当n<0时,最左边移到最右边
>>> dq
deque([7, 8, 9, 0, 1, 2, 3, 4, 5, 6], maxlen=10)
>>> dq.rotate(-4)
>>> dq
deque([1, 2, 3, 4, 5, 6, 7, 8, 9, 0], maxlen=10)
>>> dq.appendleft(-1)   # 对已满的队列添加元素时,尾部元素会被删除
>>> dq
deque([-1, 1, 2, 3, 4, 5, 6, 7, 8, 9], maxlen=10)
>>> dq.extend([11,22,33])
>>> dq
deque([3, 4, 5, 6, 7, 8, 9, 11, 22, 33], maxlen=10)
>>> dq.extendleft([10,20,30,40])
>>> dq
deque([40, 30, 20, 10, 3, 4, 5, 6, 7, 8], maxlen=10)



其他形式的队列:

queue:不同的线程可以利用这些数据类型交换信息。

multiprocessing:实现了自己的Queue,设计给进程间通信用的。

asyncio:为异步编程里的任务提供。 

heapq:把可变序列当做堆队列或者优先队列使用。