容器序列和扁平序列容器序列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函数的常用别名