学习《流畅的Python》 第2章 序列构成的数组

笛卡尔积(列表推导与生成表达式)

""" 列表推导的作用只有一个:生成列表 """
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)

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值