笛卡尔积(列表推导与生成表达式)
""" 列表推导的作用只有一个:生成列表 """
colors = ['black', 'white']
sizes = ['S', 'M', 'L']
tshirts = [(color, size) for color in colors for size in sizes] # 这里的得到的结果是先以颜色排列,再以尺码排列
print(tshirts)
for color in colors:
for size in sizes:
print((color, size))
tshirts = [(color, size) for size in sizes
for color in colors]
print(tshirts)
""" 生成器表达式:生成元组 """
colors = ['black', 'white']
sizes = ['S', 'M', 'L']
for tshirt in ('%s %s' % (c, s) for c in colors for s in sizes):
print(tshirt)
元组记录数据类型
#%%
""" 最好辨认的元组拆包形式就是平行赋值 """
lax_coordinates = (33.9425, -118.408056)
latitude, longitude = lax_coordinates # 元组拆包
print(latitude) # 33.9425
print(longitude) # -118.408056
#%%
""" *运算符可以把一个可迭代对象拆开作为函数的参数 """
print(divmod(20, 8)) # (2, 4)
t = (20, 8)
print(divmod(*t)) # (2, 4)
quotient, remainder = divmod(*t)
print(quotient, remainder) # 2 4
#%%
""" 用*来处理剩下的元素 """
rest = 0
a, b, *rest = range(5)
print(a, b, rest) # 0 1 [2, 3, 4]
a, b, *rest = range(3)
print(a, b, rest) # 0 1 [2]
a, b, *rest = range(2)
print(a, b, rest) # 0 1 []
# 在平行赋值中,*前缀只能用在一个变量名前面,但是这个变量可以出现在赋值表达式的任意位置:
body = 0
a, *body, c, d = range(5)
print(a, body, c, d) # 0 [1, 2] 3 4
#%%
""" 嵌套元组拆包 """
metro_areas = [
('Tokyo', 'JP', 36.933, (35.689722, 139.691667)),
('Delhi NCR', 'IN', 21.935, (28.613889, 77.208889)),
('Mexico City', 'MX', 20.142, (19.433333, -99.133333)),
('New York-Newark', 'US', 20.104, (40.808611, -74.020386)),
('Sao Paulo', 'BR', 19.649, (-23.547778, -46.635833))
]
print('{:15} | {:^9} | {:^9}'.format('', 'lat.', 'long.'))
fmt = '{:15} | {:9.4f} | {:9.4f}'
for name, cc, pop, (latitude, longitude) in metro_areas:
if longitude <= 0:
print(fmt.format(name, latitude, longitude))# | lat. | long.
# Mexico City | 19.4333 | -99.1333
# New York-Newark | 40.8086 | -74.0204
# Sao Paulo | -23.5478 | -46.6358
#%%
""" 定义和使用具名元组 """
from collections import namedtuple
City = namedtuple('City', 'name country population coordinates') # 类名 + 类的各个字段的名字
tokyo = City('Tokyo', 'JP', 36.933, (35.689722, 139.691667))# City(name='Tokyo', country='JP', population=36.933, coordinates=(35.689722, 139.691667))
print(tokyo)
print(tokyo.population) # 36.933
print(tokyo.coordinates) # (35.689722, 139.691667)
print(tokyo[1]) # JP
#%%
""" 具名元组的属性和方法 """
print(City._fields) # ('name', 'country', 'population', 'coordinates')
# _fileds属性是一个包含这个类所有字段名称的元组
LatLong = namedtuple('LatLong', 'lat long')
delhi_data = ('Delhi NCR', 'IN', 21.935, LatLong(28.613889, 77.208889))
delhi= City._make(delhi_data) # _make()通过接受一个可迭代的对象来生成类的实例,和City(*delhi_data)效果是一样的
print(delhi._asdict()) # OrderedDict([('name', 'Delhi NCR'), ('country', 'IN'), ('population', 21.935), ('coordinates', LatLong(lat=28.613889, long=77.208889))])
# _asdict()把具名元组以collection.OrderDict的形式返回,将元组信息友好的显示了出来
print(City(*delhi_data)) # City(name='Delhi NCR', country='IN', population=21.935, coordinates=LatLong(lat=28.613889, long=77.208889))
作为不可变列表的元组(列表或元组的方法和属性 )
"""
方法或属性 列表 元组 含义
s.__add__(s2) 有 有 s+s2
s.__iadd__(s2) 有 无 s+=s2
s.apppend(e) 有 无 尾部新加一个元素
s.clear() 有 无 删除所有元素
s.__contains__(e) 有 有 s是否包含了e
s.copy() 有 无 列表的浅拷贝
s.count(e) 有 有 e在s中出现的次数
s.__delitem__(p) 有 无 把位于p的元素删除了
s.extend(it) 有 无 把可迭代的对象it追加个s
s.__getnewargs__() 无 有 在pickle中支持更加优化的序列化
s.index(e) 有 有 在s中找到元素e第一次出现的位置
s.insert(p,e) 有 无 在位置p之前插入元素e
s.__iter__() 有 有 获取s的迭代器
s.__len__() 有 有 len(s), 元素的数量
s.__mul__(n) 有 有 s*n,n个s重复拼接
s.__imul__(n) 有 无 就地n次拼接
s.__rmul__(n) 有 有 n*s,反向拼接
s.pop([p]) 有 无 删除最后一个,或者指定p位置的元素。可选p
s.remove(e) 有 无 删除s中第一次出现的e
s.reverse() 有 无 原地把s的元素倒序排列
s.__reversed__() 有 无 返回s的倒序迭代器
s.__setitem__(p,e) 有 无 s[p]=e,把元素放在位置p上,替代原来的那个位置
s.sort([key],[reverse]) 有 无 就地对s中元素进行排序。可选key或是否倒序reverse
"""
对对象进行切片
#%%
""" 对对象进行切片 """
s = 'bicycle'
print(s[::3]) # bye 以3为间隔取值
print(s[::-1]) # elcycib 间隔为负数时反向取值
print(s[::-2]) # eccb
#%%
""" 给切片赋值 """
l = list(range(10))
print(l) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
l[2:5] = [20, 30]
print(l) # [0, 1, 20, 30, 5, 6, 7, 8, 9] 第5个元素为空了
del l[5:7]
print(l) # [0, 1, 20, 30, 5, 8, 9]
l[3::2] = [11, 22] # [0, 1, 20, 11, 5, 22, 9]
print(l)
# l[2:5] = 100
# print(l) # TypeError Traceback (most recent call last)
# # in ()
# # 8 l[3::2] = [11, 22] # [0, 1, 20, 11, 5, 22, 9]
# # 9 print(l)
# # ---> 10 l[2:5] = 100
# # 11 print(l)
# # 12 l[2:5] = [100]
# # TypeError: can only assign an iterable
# # 如果赋值对象是一个切片,那么赋值语句的右侧必须是个可迭代对象。即便只有单独的一个值,也要转换成可迭代序列。
l[2:5] = [100]
print(l) # [0, 1, 100, 22, 9]
#%%
""" 对序列使用+和* """
l = [1, 2, 3]
print(l * 5) # [1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3]
print(5 * 'abcd') # abcdabcdabcdabcdabcd
# 注意:+和*都不修改原有的操作对象,而是构造一个全新的序列。
建立由列表生成的列表
#%%
""" 建立由列表组成的列表 """
# 示例2-12
board = [['_'] * 3 for i in range(3)] # 每一个i生成一个列表(包含三个元素),之间是有区别的
print(board) # [['_', '_', '_'], ['_', '_', '_'], ['_', '_', '_']]
# '_'改为i结果变为[[0, 0, 0], [1, 1, 1], [2, 2, 2]]
board[1][2] = 'X'
print(board) # [['_', '_', '_'], ['_', '_', 'X'], ['_', '_', '_']]
board = []
for i in range(3):
row = ['_'] * 3 # 每次迭代都新建一个列表
board.append(row)
board[2][0] = 'X'
print(board) # [['_', '_', '_'], ['_', '_', '_'], ['X', '_', '_']]
# 示例2-13
weird_board = [['_'] * 3] * 3 # 将一个列表直接复制三份,而不是一一生成。这三个列表其实一模一样。
print(weird_board) # [['_', '_', '_'], ['_', '_', '_'], ['_', '_', '_']]
weird_board[1][2] = '0'
print(weird_board) # [['_', '_', '0'], ['_', '_', '0'], ['_', '_', '0']]
row = ['_'] * 3
board = []
for i in range(3):
board.append(row) # 追加了同一个对象三次
board[2][0] = 'X'
print(board) # [['X', '_', '_'], ['X', '_', '_'], ['X', '_', '_']]
序列的增量赋值
'''变量名会不会被关联到新的对象,完全取决于这个对象有没有实现__iadd__和__imul__方法'''
#%%
'''*=在可变序列和不可变序列上的作用'''
l = [1, 2, 3]
print(id(l)) # 1750198341064
l *= 2
print(l) # [1, 2, 3, 1, 2, 3]
print(id(l)) # 1750198341064
t = (1, 2, 3)
print(id(t)) # 1750198083872
t *= 2
print(t) # (1, 2, 3, 1, 2, 3)
print(id(t)) # 1750198048424
# 对于不可变序列进行重复拼接操作的话,效率会很低,因为每次有一个新对象,
# 儿解释器需要把原来的对象中的元素先复制到新对象中,再追加新的元素。
#%%
'''一个谜题'''
t = (1, 2, [30, 40])
t[2] += [50, 60] # 运行这段代码会抛出异常
#%%
print(t) # 打印出t会发现t[2]已经被改动了,(1, 2, [30, 40, 50, 60])
#%%
'''s[a] += b背后的字节码'''
import dis
print(dis.dis('s[a] += b'))
""" 1 0 LOAD_NAME 0 (s)
2 LOAD_NAME 1 (a)
4 DUP_TOP_TWO
6 BINARY_SUBSCR # 将s[a]的值存入TOS(Top of Stack, 栈的顶端)
8 LOAD_NAME 2 (b)
10 INPLACE_ADD # 计算
12 ROT_THREE
14 STORE_SUBSCR # s[a] = TOS赋值。这一步失败,是因为s是不可变的元组
16 LOAD_CONST 0 (None)
18 RETURN_VALUE
None """
'''3点总结:
不要把可变对象放在元组里。
增量赋值不是一个原子操作。虽然抛出了异常,但完成了操作。
查看Python字节码并不难,而且对我们了解代码背后的运行机制很有帮助。'''
list.sort方法和内置函数sorted
"""
list.sort()会就地排序列表,也就是说不会把原列表赋值一份。这个方法返回None,提醒本方法不会新建一个列表。
Python的一个惯例:如果一个函数或者方法对对象进行的是就地改动,那它就应该返回None。
因此,调用者的参数发生了变化,但未产生新对象。例如,random,shuffle函数也是这样。
弊端:因为不产生新对象,所以不能将其串联起来。
与list.sort相反的内置函数sorted,它会新建一个列表作为返回值。这个方法可以接受任何形式的可迭代的对象作为参数。
不管sorted接受的是怎样的参数,它最后都会返回一个列表。
list.sort()和sorted,都有两个可选的关键字参数:
reverse
如果被设定为True,被排序的序列元素会以降序输出,默认为False。
key
一个只有一个参数的函数。这个函数会被用在序列的每一个元素上。所产生的的结果将是排序徐算法依赖的对比关键字。
比如,key=str.lower来实现忽略大小写的排序,或者用key=len进行基于字符串长度的排序。
默认值是恒等函数(identity function),默认用元素自身的值来排序。
"""
#%%
fruits = ['grape', 'raspberry', 'apple', 'banana']
a = sorted(fruits)
print(a) # ['apple', 'banana', 'grape', 'raspberry']
print(fruits) # ['grape', 'raspberry', 'apple', 'banana']
print(sorted(fruits, reverse=True)) # ['raspberry', 'grape', 'banana', 'apple']
print(sorted(fruits, key=len)) # ['grape', 'apple', 'banana', 'raspberry']
print(sorted(fruits, key=len, reverse=True)) # ['raspberry', 'banana', 'grape', 'apple'] 从相对位置可以看出Python使用的是稳定排序算法
print(fruits) # ['grape', 'raspberry', 'apple', 'banana']
b = fruits.sort()
print(fruits.sort()) # None
print(fruits) # ['apple', 'banana', 'grape', 'raspberry'] 可以看出列表本身被排序了,为就地操作
用bisect来处理已排序的序列
""" bisect模块包含两个主要函数,bisect和insort,两个函数都利用二分查找算法来在有序序列中查找或插入元素 """
#%%
'在有序序列中用bisect查找某个元素的插入位置'
import bisect
import sys
HAYSTACK = [1, 4, 5, 6, 12, 15, 20, 21, 23, 23, 26, 29, 30]
NEEDLES = [0, 1, 2, 5, 8, 10, 22, 23, 29, 30, 31]
ROW_FMT = '{0:2d} @ {1:2d} {2}{0:<2d}' # 表示 needle的值, needleHAYSTACK中的索引
# 占两个宽度并向左对齐,每次缩进战占两个宽度
def demo(bisect_fn):
for needle in reversed(NEEDLES): # 颠倒过来打印出来更美观
position = bisect_fn(HAYSTACK, needle) # needle在HAYSTACK中的索引,这里用特定的函数计算了needle出现的位置
offset = position * ' |' # 缩进,和索引大小一样的缩进长度还要加上 '|'
print(ROW_FMT.format(needle, position, offset)) # 在特定的位置打印元素
if __name__ == "__main__": # 运行自身时运行,被导入时不运行
if sys.argv[-1] == 'left':
bisect_fn = bisect.bisect_left
else:
bisect_fn = bisect.bisect # 等于bisect_right
print("DEMO", bisect_fn.__name__)
print('haystack -> ', ''.join('%2d' % n for n in HAYSTACK))
demo(bisect_fn)
# result:
# DEMO bisect_right
# haystack -> 1 4 5 6121520212323262930
# 31 @ 13 | | | | | | | | | | | | |31
# 30 @ 13 | | | | | | | | | | | | |30
# 29 @ 12 | | | | | | | | | | | |29
# 23 @ 10 | | | | | | | | | |23
# 22 @ 8 | | | | | | | |22
# 10 @ 4 | | | |10
# 8 @ 4 | | | |8
# 5 @ 3 | | |5
# 2 @ 1 |2
# 1 @ 1 |1
# 0 @ 0 0
#
#
# bisect有两个可选参数,lo和hi,即二分查找的两端。
# bisect是bisect_right的别名,后者有一个姊妹函数叫bisect_left。区别在于插入元素位置的不同:
# DEMO bisect_left
# haystack -> 1 4 5 6121520212323262930
# 31 @ 13 | | | | | | | | | | | | |31
# 30 @ 12 | | | | | | | | | | | |30
# 29 @ 11 | | | | | | | | | | |29
# 23 @ 8 | | | | | | | |23
# 22 @ 8 | | | | | | | |22
# 10 @ 4 | | | |10
# 8 @ 4 | | | |8
# 5 @ 2 | |5
# 2 @ 1 |2
# 1 @ 0 1
# 0 @ 0 0
#
#%%
'''根据一个分数,找出它对应的成绩'''
def grade(score, breakpoints=[60, 70, 80, 90], grades='FDCBA'):
i = bisect.bisect(breakpoints, score)
return grades[i]
print([grade(score) for score in [33, 99, 77, 70, 89, 90, 100]]) # ['F', 'A', 'C', 'C', 'B', 'A', 'A']
#%%
'''用bisect.insort插入新元素'''
'''insort(seq, item)把变量item掺入到序列seq中,并能保持seq的升序顺序。'''
import bisect
import random
size = 7
random.seed(1234)
my_list = []
for i in range(size):
new_item = random.randrange(size*2)
bisect.insort(my_list, new_item)
print('%2d ->' % new_item, my_list) # 12 -> [12]
# 7 -> [7, 12]
# 1 -> [1, 7, 12]
# 0 -> [0, 1, 7, 12]
# 1 -> [0, 1, 1, 7, 12]
# 12 -> [0, 1, 1, 7, 12, 12]
# 9 -> [0, 1, 1, 7, 9, 12, 12]
当列表不是首选时
""" 数组 """
#%%
'''一个浮点型数组的创建、存入文件和从文件读取的过程'''
from array import array # 引入array类型
from random import random
floats = array('d', (random() for i in range(10**7))) # 利用一个可迭代对象来建立一个双精度浮点数组(类型码是'd')
# 这里用的可迭代对象是一个生成器表达式
print(floats[-1]) # 查看数组的最后一个元素 0.44800656386657145
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(floats2[-1]) # 检查数组的最后一个元素 0.44800656386657145
print(floats2 == floats) # 检查两个数组的内容是不是完全一样 True
#%%
'''内存视图-memoryview'''
numbers = array('h', [-2, -1, 0, 1, 2])
memv = memoryview(numbers) # 创建一个memoryview
print(len(memv)) # 5
print(memv[0]) # -2 memv里的五个元素跟数组里的没有区别
memv_oct = memv.cast('B') # 把memv的类型转化成'B'类型,无符号字符型。
print(memv_oct.tolist()) # [254, 255, 255, 255, 0, 0, 1, 0, 2, 0] 以列表形式查看
memv_oct[5] = 4
print(numbers) # array('h', [-2, -1, 1024, 1, 2])
#%%
'''Numpy和SciPy'''
import numpy
a = numpy.arange(12) # 新建一个0-11的整数的数组
print(a) # [ 0 1 2 3 4 5 6 7 8 9 10 11]
print(type(a)) # <class 'numpy.ndarray'>
print(a.shape) # (12,)
a.shape = 3, 4
print(a) # [[ 0 1 2 3]
# [ 4 5 6 7]
# [ 8 9 10 11]]
# [ 8 9 10 11]
print(a[2]) # [ 8 9 10 11]
print(a[2, 1]) # 9
print(a[:, 1]) # [1 5 9]
print(a.transpose()) # [[ 0 4 8]
# [ 1 5 9]
# [ 2 6 10]
# [ 3 7 11]]
#%%
'''双向队列和其他形式的队列'''
from collections import deque
dq = deque(range(10), maxlen=10) # maxlen代表队列可以容纳的元素数量,一旦设定不可修改
print(dq) # deque([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], maxlen=10)
dq.rotate(3) # n>0队列最右边的元素会移动到最左边
print(dq) # deque([7, 8, 9, 0, 1, 2, 3, 4, 5, 6], maxlen=10)
dq.rotate(-4)
print(dq) # deque([1, 2, 3, 4, 5, 6, 7, 8, 9, 0], maxlen=10)
dq.appendleft(-1)
print(dq) # deque([-1, 1, 2, 3, 4, 5, 6, 7, 8, 9], maxlen=10)
dq.extend([11, 22, 33])
print(dq) # deque([3, 4, 5, 6, 7, 8, 9, 11, 22, 33], maxlen=10)
dq.extendleft([10, 20, 30, 40]) # 把迭代器里的元素逐个添加到队列的最左边
print(dq) # deque([40, 30, 20, 10, 3, 4, 5, 6, 7, 8], maxlen=10)