容器序列和扁平序列容器序列list, tuple, collections.deque这些容器内能存放不同数据类型的数据

扁平序列str, bytes, bytearray, memoryview和array.array只能存放同一数据类型

容器内容的可变与不可变可变: list, bytearray, array.array, collections.deque, memoryview

不可变tuple, str, bytes

列表推导

列表推导(list comprehension)简称为listcomps

L = [x for x in range(100)]

M = [x*y for x in range(10) for y in range(11, 20) ]

print(L)

print(M)虽然老生常谈了,但是额外说一句从后往前读非常容易理解

使用原则:用列表推导来创建新的列表,并且尽量保持简短

python会忽略代码里的[], {}, ()中的换行

列表推导不一定比map()和filter()的组合速度慢

生成器表达式生成器表达式的语法跟列表推导差不多,只不过是把方括号[]变成()

生成器逐个产生元素, 不会像列表推导一样一次性占用内存, 因此使用生成器表达式可以一定程度上节省内存

*运算符用于拆包

*运算符可以把一个可迭代对象拆开为函数的参数, 用*args来获取不确定数量的参数也是一种经典的写法

a, b, *rest = range(5)

print(a, b, rest)

a, b, *rest = range(3)

print(a, b, rest)

a, b, *rest = range(2)

print(a, b, rest)

a, *b, c, d = range(5)

print(a, b, c, d)

*a, b, c, d = range(5)

print(a, b, c, d)

用python打印表格的套路(str.format()函数)

浮点数指定输出位数以及正负号

fmt = '{:.2f}' # 保留两位小数

print(fmt.format(3.1415926))

>>>3.14

fmt = '{:+.2f}' # 显示正负号

print(fmt.format(3.1415))

print(fmt.format(-520.1314))

>>>+3.14

>>>-520.13

指定长度输出与对齐方式, 格式{:*>n} - >: 右对齐(即靠右放置,左边填充) - <: n:>

fmt = '{:x>4}' # 长度为4,靠右放置,左填充x

print(fmt.format(2))

>>>xxx2

fmt = '{:<4}|' # 长度为4,靠左放置,右填充空白(使用|仅仅用来比较,方便演示)

print(fmt.format(2))

>>>2 |

fmt = '|{:^5}|' # 中间对齐

print(fmt.format(123))

>>>| 123 |

以此基础可以打印漂亮的表格

print('{:15} | {:^9} | {:^9}'.format('', 'lat.', 'long.'))

fmt = '{:15} | {:9.4f} | {:9.4f}'

print(fmt.format('Beijing', 1.234, 5.6789))

print(fmt.format('ShangHai', 12.3456, 789.012))

顺便发现了用来打印表格的库prettytable

namedtuple

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

from collections import namedtuple

Point = namedtuple('Point', 'x y')

p1 = Point(1, 2)

print(p1)

>>>Point(x=1, y=2)

等价于

from collections import namedtuple

li = ['x', 'y']

Point = namedtuple('Point', li)

p1 = Point(1, 2)

print(p1)

>>>Point(x=1, y=2)

切片

在pyton中支持切片的有list, tuple, str 切片的格式为DS[start:stop:step], 左闭右开, 长度为stop - start, 每间隔step位取DS内的元素

li = list(range(10))

L1 = li[:3] # 被取元素的下标范围为[0, 3)

print(L1)

>>>[0, 1, 2]

li = list(range(10))

L2 = li[3:] # 被取元素的下标范围为[3, 10)

print(L2)

>>>[3, 4, 5, 6, 7, 8, 9]

li = list(range(10))

L3 = li[::2] # 每间隔2取元素

print(L3)

>>>[0, 2, 4, 6, 8]

li = list(range(10))

L4 = li[::-1] # 逆置(reverse)

print(L4)

>>>[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

s1 = 'I love you'

print(s1[::-1])

>>>uoy evol I

给切片赋值

li = list(range(10))

li[2:5] = [20, 30]

print(li)

li[2:5] = [20, 30, 40]

print(li)

>>>[0, 1, 20, 30, 5, 6, 7, 8, 9]

>>>[0, 1, 20, 30, 40, 6, 7, 8, 9]

li = list(range(10))

del li[3:]

print(li)

>>>[0, 1, 2]

序列的增量赋值*=与+=

类能实现+=操作需要实现魔法函数__iadd__(就地加法), 如果没有实现将退一步调用__add__ 如果a实现了__iadd__方法, 则a += b将原地进行, 即将b加入到a中, 如果没实现则a = a + b, 之前的a的内存被释放(不同id)

python自带的可变容器都支持+=和*=操作

*=对应__imul__, 思想类似

一个有意思的例子: python tup = (1, 2, [3, 4]) tup[2] += [5, 6] 运行上面的代码会发生什么?

上面的例子告诉我们: - 不要把可变对象放在元组里 - 增量赋值不是一个原子操作, 虽然抛出了异常但还是完成了操作

list.sort()和内置函数sorted()

list.sort()是就地排序, 返回值是None(一种思想), 从python3.4开始不再支持list.sorted sorted()会新建一个列表返回 两者都有两个可选的关键字参数, reverse和key, 重点讲key, key为一个只有一个参数的函数, 类似于c++里的cmp, 只传递函数名, 省略括号

bisect

bisect.bisect(haystack, needle), 在haystack(干草堆)里搜索needle(针)的位置, needle插入到这个位置后, haystack还能保持升序. 要求haystack原本有序, 原理为二分查找

bisect.insort(haystack, needle)为找到位置后并插入

不要滥用列表list存放大量的浮点数, array的效率更高, 同理, 如果我们需要一个只包含数字的列表, 那么array.array比list更高效

频繁的对序列做先进先出操作, deque(双端队列)的速度更快

书上一个给人启发的例子

from array import array
from random import random
floats = array('d', (random() for i in range(10**7)))
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()
print(floats == floats2)
memoryview
deque

list删除第一个元素或者在第一个元素之前添加一个元素很费时, O(n)? 推荐使用deque, deque是线程安全的

本章的一些不好起标题的知识点交换两个变量的值a, b = b, a

如果做的是国际化软件, 那么_可能就不是一个理想的占位符, 因为它是gettext.gettext函数的常用别名