python进阶练习(长期更新中。。。)

一、数据结构

第一章  序列构成的数组

1.1 内置序列类型概述

容器序列:list、tuple和collections.deque这些序列能存放不同类型的数据。

扁平序列:str、bytes、bytearray、memoryview和array.array,这些序列只能容纳一种类型。

可变序列:list、bytearray、array.array、collections.deque和memoryview。

不可变序列:tuple、str和bytes。

1.2 列表推导和生成器表达式
1.2.1 列表推导和可读性

2-1 把字符串变成Unicode码位的列表

# 方法一
sysmbols = '$¥&*#@!'
codes = []
for symbol in sysmbols:
    codes.append(ord(symbol))
print(codes)

# 方法二
sysmbols = '$¥&*#@!'
codes = [ord(symbol) for symbol in sysmbols]
print(codes)

# 结果
[36, 65509, 38, 42, 35, 64, 33]
[36, 65509, 38, 42, 35, 64, 33]

句法提示:python会忽略代码里[]、{}、()中的换行,因此如果你的代码里有多行的列表、列表推导、生成器表达式、字典这一类的,可以省略不太好看的续行符\。

2-2 列表推导不会再有变量泄露的问题

# 列表推导、生成器表达式、集合推导、字典推导,在python3中有自己局部作用域,不会影响同名变量。
x = 'ABC'
dummy = [ord(x) for x in x]
print(x)
print(dummy)

# 结果
ABC
[65, 66, 67]
1.2.2 列表推导同filter和map的比较

2-3 列表推导和map/filter组合结果相同,明显列表推导更简单

sysmbols = '$¥&*#@!'
beyond_ascii = [ord(s) for s in sysmbols if ord(s) > 127]
print(beyond_ascii)

beyond_ascii2 = list(filter(lambda c: c > 127, map(ord, sysmbols)))
print(beyond_ascii2)

# 结果
[65509]
[65509]
1.2.3 笛卡儿积

2-4 使用列表推导计算笛卡儿积

colors = ['black', 'white']
sizes = ['S', 'M', 'L']
tshirts = [(color, size) for size in sizes
                        for color in colors]
print(tshirts)

# 结果
[('black', 'S'), ('white', 'S'), ('black', 'M'), ('white', 'M'), ('black', 'L'), ('white', 'L')]

列表推导的作用只有一个:生成列表 

1.2.4 生成器表达式

2-5 用生成器表达式初始化元组和数组

sysmbols = '$¥&*#@!'
tup = tuple(ord(symbol) for symbol in sysmbols)
print(tup)

import array
arr = array.array('I', (ord(symbol) for symbol in sysmbols))
print(arr)

# 结果
(36, 65509, 38, 42, 35, 64, 33)
array('I', [36, 65509, 38, 42, 35, 64, 33])

2-6 使用生成器表达式计算笛卡儿积

生成器表达式逐个产出元素,不会一次性产出所有列表,避免额外的内存占用。

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

# 结果
black, S,
black, M,
black, L,
white, S,
white, M,
white, L,
1.3 元组不仅仅是不可变的列表
1.3.1 元组和记录

2-7 把元组用作记录

lax_coordinates = (33.9425, -118.408056)
city, year, pop, chg, area = ('Tokyo', 2003, 32450, 0.66, 8041)
traveler_ids = [('USA', '31195855'), ('BRA', 'CE342567'), ('ESP', 'XDA205856')]
for passport in sorted(traveler_ids):    #迭代过程中,passport变量被绑定到每个元组上
    print('%s/%s' %passport)
for country, _ in traveler_ids:    #拆包(unpacking),元组中第二个元组没用,用占位符“_”
    print(country)

# 结果
BRA/CE342567
ESP/XDA205856
USA/31195855
USA
BRA
ESP
1.3.2 元组拆包
lax_coordinates = (33.9425, -118.408056)
latitude, longitude = lax_coordinates   # 元组拆包
print(latitude, longitude)

dm = divmod(20, 8)
print(dm)

t = (20, 8)
dm2 = divmod(*t)    # 用*运算符把一个可迭代对象拆开作为函数的参数
print(dm2)

quotient, remainder = divmod(*t)
print(quotient, remainder)

# 结果
33.9425 -118.408056
(2, 4)
(2, 4)
2 4

os.path.split()函数会返回以路径和最后一个文件名组成的元组(path, last_part) 

import os
path, filename = os.path.split('D:\pythonProject\Fluent_Python\Data_Structure\\array\\tuple.py')
print(path)
print(filename)

# 结果
D:\pythonProject\Fluent_Python\Data_Structure\array
tuple.py

用 * 来处理剩下的元素

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, *body, c, d = range(5)   # 在平行赋值中,*前缀只能用在一个变量名前,但是这个变量可以出现在复制表达式的任意位置
print(a, body, c, d)
*head, b, c, d = range(5)
print(head, b, c, d)

# 结果
0 1 [2, 3, 4]
0 1 [2]
0 1 []
0 [1, 2] 3 4
[0, 1] 2 3 4
1.3.3 嵌套元组拆包

2-8 用嵌套元组来获取经度

metro_areas = [
    ('Tokyo', 'JP', 36.933, (350689722, 139.691667)),   # 每个元组有4个元素,其中最后一个元素时一对坐标
    ('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('city', '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))

# 结果
city            |   lat.    |   long.  
Mexico City     |   19.4333 |  -99.1333
New York-Newark |   40.8086 |  -74.0204
Sao Paulo       |  -23.5478 |  -46.6358
1.3.4 具名元组

2-9 定义和使用具名元组

from collections import namedtuple

City = namedtuple('City', 'name country population coordinates')    # 创建一个具名元组需要两个参数,一个类名,一个类的各个字段的名字
tokyo = City('Tokyo', 'JP', 36.933, coordinates=(35.689722, 139.691667))
print(tokyo.population)
print(tokyo.coordinates)
print(tokyo[1])

#结果
36.933
(35.689722, 139.691667)
JP

2-10 具名元组的属性和方法(接续2-9)

print(City._fields)  # _fields属性时一个包含这个类所有字段名称的元组
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())  # _asdict()把具名元组以collections.OrderedDict的形式返回,可以用它把元组里的信息友好的呈现
for key, value in delhi._asdict().items():
    print(key + ':', value)

#结果
('name', 'country', 'population', 'coordinates')
{'name': 'Delhi NCR', 'country': 'IN', 'population': 21.935, 'coordinates': Latlong(lat=28.613889, long=77.208889)}
name: Delhi NCR
country: IN
population: 21.935
coordinates: Latlong(lat=28.613889, long=77.208889)
1.3.5 作为不可变列表的元组
列表元组
s._add_(s2)**s + s2,拼接
s._iadd_(s2)*s += s2,就地拼接
s.append(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._getitem_(p)**s[p],获取位置p的元素
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)*s *= n,就地重复拼接
s._rmul_(n)**n * s,反向拼接
s.pop([p])*删除最后或者是位于p的元素,并返回它的值
s.remove(e)*删除s中的第一次出现的e
s.reverse()*就地把s的元素倒序排列
s._reversed_()*返回s的倒序迭代器
s._setitem_(p, e)*s[p] = e,把元素e放在位置p,替代已经在那个位置的元素
s.sort([key], [reverse])*就地对s中的元素进行排序,可选的参数有键(key)和是否倒序

1.4 切片
1.5 对序列使用 + 和 *
建立由列表组成的列表

2-12 一个包含3个列表的列表,嵌套的3个列表各自有3个元素来代表井字游戏的一行方块。

board = [['_'] * 3 for i in range(3)]   #建立一个包含3个列表的列表,被包含的3个列表各自有3个元素
print(board)
board[1][2] = 'X'   # 把第1行第2列的元素标记为X
print(board)

# 结果
[['_', '_', '_'], ['_', '_', '_'], ['_', '_', '_']]
[['_', '_', '_'], ['_', '_', 'X'], ['_', '_', '_']]

2-13 含有3个指向同一对象的引用的列表是毫无用处的

weird_board = [['_'] * 3] * 3   # 外面的列表其实包含3个指向同一个列表的引用,当不做修改的时候,看起来都还好
print(weird_board)
weird_board[1][2] = '0'     # 试图标记第1行第2列的元素,就立马暴露列表内的3个引用指向同一个对象
print(weird_board)

# 结果
[['_', '_', '_'], ['_', '_', '_'], ['_', '_', '_']]
[['_', '_', '0'], ['_', '_', '0'], ['_', '_', '0']]
1.6 序列的增量赋值

例 *= 在可变和不可变序列上的作用

l = [1, 2, 3]  
print(id(l))    # 列表最开始的id
l *= 2
print(id(l))    # 运用增量乘法后,列表的id没变,新元素追加到列表上
t = (1, 2, 3)
print(id(t))    # 元组最开始的id
t *= 2
print(id(t))    # 运用增量乘法后,新的元组被创建

# 结果
2744821281408
2744821281408
2744824891520
2744821338496
一个关于+=的谜题 

注意:不要把可变对象放在元组里面。

2-14 一个谜题

>>> t = (1,2,[30,40])
>>> t[2]+=[50,60]    # 因为tuple不支持对它的元素赋值,所以会抛出TypeError异常
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>> t
(1, 2, [30, 40, 50, 60])    # t[2]被改动了
1.7 list.sort方法和内置函数sorted

例 list.sort和sorted和它们的关键字参数

fruits = ['grape', 'raspberry', 'apple', 'banana']
print(sorted(fruits))   # 新建一个按照字母排序的字符串列表
print(fruits)   # 原列表没有变化
print(sorted(fruits, reverse=True))     # 按照字母降序排列
print(sorted(fruits, key=len))      # 新建一个按照长度排序的字符串列表
print(sorted(fruits, key=len, reverse=True))    # 按照长度降序排列的结果
print(fruits)   # 原列表没有变化   
fruits.sort()   # 对原列表就地排序,返回值None被控制台忽略
print(fruits)   # fruits本身被排序

# 结果
['apple', 'banana', 'grape', 'raspberry']
['grape', 'raspberry', 'apple', 'banana']
['raspberry', 'grape', 'banana', 'apple']
['grape', 'apple', 'banana', 'raspberry']
['raspberry', 'banana', 'grape', 'apple']
['grape', 'raspberry', 'apple', 'banana']
['apple', 'banana', 'grape', 'raspberry']
1.8 用bisect来管理已排序的序列
1.8.1 用bisect来搜索

bisect(haystack,needle)在haystack(干草堆)里搜索needle(针)的位置,需要满足的条件是,把needle插入这个位置后,haystack还能保持升序。也就是说这个函数返回的位置前面的值,都小于或等于needle的值。其中haystack必须是一个有序的序列。

2.17 在有序序列中用bisect查找某个元素的插入位置

import bisect
import sys
HAYSTACK = [1, 4, 5, 6, 8, 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}'
def demo(bisect_fn):
    for needle in reversed(NEEDLES):
        position = bisect_fn(HAYSTACK, needle)  # 用特定的bisect函数来计算元素应该出现的位置
        offset = position * '    |'   # 利用该位置来计算出需要几个分隔符号
        print(ROW_FMT.format(needle, position, offset))     # 把元素和其应该出现的位置打印出来

if __name__ == '__main__':
    if sys.argv[-1] == 'left':      #根据执行命令最后一个参数来选用bisect函数,例:python bisect_demo.python left
        bisect_fn = bisect.bisect_left
    else:
        bisect_fn = bisect.bisect
    print('DEMO:', bisect_fn.__name__)      # 把选定的函数打印出来
    print('haystack ->', '   '.join('%2d' % n for n in HAYSTACK))
    demo(bisect_fn)

# 结果
DEMO: bisect_right
haystack ->  1    4    5    6    8   12   15   20   21   23   23   26   29   30
31 @ 14      |    |    |    |    |    |    |    |    |    |    |    |    |    |31
30 @ 14      |    |    |    |    |    |    |    |    |    |    |    |    |    |30
29 @ 13      |    |    |    |    |    |    |    |    |    |    |    |    |29
23 @ 11      |    |    |    |    |    |    |    |    |    |    |23
22 @  9      |    |    |    |    |    |    |    |    |22
10 @  5      |    |    |    |    |10
 8 @  5      |    |    |    |    |8 
 5 @  3      |    |    |5 
 2 @  1      |2 
 1 @  1      |1 
 0 @  0  0 

# 执行python bisect_demo.python left结果
DEMO: bisect_left
haystack ->  1    4    5    6    8   12   15   20   21   23   23   26   29   30
31 @ 14      |    |    |    |    |    |    |    |    |    |    |    |    |    |31
30 @ 13      |    |    |    |    |    |    |    |    |    |    |    |    |30
29 @ 12      |    |    |    |    |    |    |    |    |    |    |    |29
23 @  9      |    |    |    |    |    |    |    |    |23
22 @  9      |    |    |    |    |    |    |    |    |22
10 @  5      |    |    |    |    |10
 8 @  4      |    |    |    |8
 5 @  2      |    |5
 2 @  1      |2
 1 @  0  1
 0 @  0  0

2-18 根据一个分数,找到它所对应的成绩

import bisect

def grade(score, breakpoints=[60, 70, 80, 90], grades='FDCBA'):
    i = bisect.bisect(breakpoints, score)
    print(grades[i])
if __name__ == '__main__':
    [grade(score) for score in [33, 99, 77, 70, 89, 90, 100]]

# 结果
F
A
C
C
B
A
A
2.8.2 用bisect.insort插入新元素

insort(seq, item)把变量item插入到序列seq中,并能保持seq的升序顺序。

2-19 insort可以保持有序序列的顺序

import bisect
import random

SIZE = 7
random.seed(1729)
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)

# 结果
10 -> [10]
 0 -> [0, 10]
 6 -> [0, 6, 10]
 8 -> [0, 6, 8, 10]
 7 -> [0, 6, 7, 8, 10]
 2 -> [0, 2, 6, 7, 8, 10]
10 -> [0, 2, 6, 7, 8, 10, 10]
1.9 当列表不是首选时
1.9.1 数组

2-20 一个浮点型数组的创建、存入文件和从文件读取的过程

from array import array
from random import random

floats = array('d', (random() for i in range(10 ** 7)))     # 利用一个可迭代对象生成器表达式来建立一个双精度浮点数组(类型码是’d')
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)   # 把1000万个浮点数从二进制文件里读取出来
fp.close()
print(floats2[-1])      # 查看新数组的最后一个元素
print(floats2 == floats)        # 检查两个数组的内容是不是完全一样

# 结果
0.5099803693183845
0.5099803693183845
True
列表数组
s.__add__(s2)**s + s2,拼接
s.__iadd__(s2)**s += s2,就地拼接
s.append(e)**在尾部添加一个元素
s.byteswap*翻转数组内每个元素的字节序列,转换字节序
s.clear()*删除所有元素
s.__contains__(e)**s是否含有e
s.copy()*对列表浅复制
s.__copy()*对copy.copy的支持
s.count(e)**s中e出现的次数
s.__deepcopy__()*对copy.deepcopy的支持
s.__delitem__(p)**删除位置p的元素
s.extend(it)**将可迭代对象it里的元素添加到尾部
s.frombytes(b)*将压缩成机器值得字节序列读出来添加到尾部
s.fromfile(f,n)*将二进制文件f内含有机器值读出来添加到尾部,最多添加n项
s.fromlist(l)*将列表里的元素添加到尾部,如果其中任何一个元素导致了TypeError异常,那么所有的添加都会取消
s.__getitem__(p)**s[p],读取位置p得元素
s.index(e)**找到e在序列中第一次出现的位置
s.insert(p,e)**在位于p得元素之前插入元素e
s.itemsize*数组中每个元素得长度是几个字节
s.__iter__()**返回迭代器
s.__len__()**len(s),序列的长度
s.__mul__(n)**s * n,重复拼接
s.__imul__(n)**s *= n,就地重复拼接
s.__rmul__(n)**n * s,反向重复拼接*
s.pop([p])**删除位于p得值并返回这个值,p得默认值是一个元素的位置
s.remove(e)**删除序列里第一次出现的e元素
s.reverse(e)**就地调转序列中元素的位置
s.__reverse__()*返回一个从尾部开始扫描元素的迭代器
s.__setitem__(p,e)**s[p] = e,把位于p位置的元素替换成e
s.sort([key], [reverse])*就地排序序列,可选参数有key和reverse
s.tobytes()*把所有元素的机器值用bytes对象的形式返回
s.tofile(f)*把所有元素以机器值得形式写入一个文件
s.tolist()*把数组转换成列表,列表里的元素类型是数字对象
s.typecode*返回只有一个字符的字符串,代表数组元素在C语言中得类型
1.9.2 内存视图

2-21 通过改变数组中的一个字节来更新数组里某个元素的值。

import array

numbers = array.array('h', [-2, -1, 0, 1, 2])   # 利用含有5个短整型有符号整数的数组创建一个memoryview
memv = memoryview(numbers)
print(len(memv))
print(memv[0])      # memv里的5个元素跟数组里的没有区别
memv_oct = memv.cast('B')   # 创建一个memv_oct,把memv里的内容转换成‘B’类型,无符号字符
print(memv_oct.tolist())    # 以列表得形式查看memv_oct的内容,出现10个元素,可以研究存储知识,源码,补码
memv_oct[5] = 4     # 把位于位置5的字节赋值成4
print(numbers)      # 因为把占2个字节的整数的高位字节改成4,所以这个有符号整数的值就变成了1024

# 结果
5
-2
[254, 255, 255, 255, 0, 0, 1, 0, 2, 0]
array('h', [-2, -1, 1024, 1, 2])
1.9.3 NumPy和SciPy

2-22 对numpy.ndarray的行和列进行基本操作

import numpy

a = numpy.arange(12)    # 新建一个0~11的整数numpy.ndarray
print(a)
print(type(a))
print(a.shape)      # 数组的维度,一个一维的,有12个元素
a.shape = 3, 4      # 把数组变成2维的
print(a)
print("------------")
print(a[2])         # 打印出第2行
print("----------")
print(a[2, 1])      # 打印第2行第1列的元素
print("----------")
print(a[:, 1])      # 打印第1列
print("----------")
print(a.transpose())    # 把行和列交换,得到一个新数组

# 结果
[ 0  1  2  3  4  5  6  7  8  9 10 11]
<class 'numpy.ndarray'>
(12,)
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]]
------------
[ 8  9 10 11]
----------
9
----------
[1 5 9]
----------
[[ 0  4  8]
 [ 1  5  9]
 [ 2  6 10]
 [ 3  7 11]]
1.9.4 双向队列和其他形式的队列
from collections import deque

dq = deque(range(10), maxlen=10)    # maxlen是一个可选参数,代表这个队列可以容纳的元素的数量,一旦设定,这个属性就不能修改了
print(dq)
dq.rotate(3)            # 队列的旋转操作接受一个n,当n>0时,队列的最右边的n个元素会被移动到队列的左边。当n<0时,最左边的n个元素会被移动到右边
print(dq)
dq.rotate(-4)           
print(dq)
dq.appendleft(-1)       # 当试图会一个已满的队列做头部添加操作的时候,它尾部的元素会被删除掉。注意在下一行里,元素0被删除了
print(dq)
dq.extend([11, 22, 33]) # 在尾部添加3个元素的操作会挤掉-1、1、2
print(dq)
dq.extendleft([10, 20, 30, 40])     # extendleft(iter)方法会把迭代器里的元素逐个添加到双向队列的左边,因此迭代器里的所有元素会逆序出现在队列里
print(dq)

# 结果
deque([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], maxlen=10)
deque([7, 8, 9, 0, 1, 2, 3, 4, 5, 6], maxlen=10)
deque([1, 2, 3, 4, 5, 6, 7, 8, 9, 0], maxlen=10)
deque([-1, 1, 2, 3, 4, 5, 6, 7, 8, 9], maxlen=10)
deque([3, 4, 5, 6, 7, 8, 9, 11, 22, 33], maxlen=10)
deque([40, 30, 20, 10, 3, 4, 5, 6, 7, 8], maxlen=10)

第二章  字典和集合

第三章  文本和字节序列

二、把函数视作对象

三、面向对象惯用法

四、控制流程

五、元编程

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值