饶头训练
列表、字典、集合解析
sorted的key参数
from itertools import islice
from collections import namedtuple
from collections import Counter
from collections import OrderedDict
from collections import deque
from collections import Iterable, Iterator ,Generator
from functools import reduce
re.split()
re.sub()
my_str.join()
my_str.maketrans() my_str.translate()
反迭代协议 __reversed__
from decimal import Decimal
from itertools import chain
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 数据构建与解析
# 筛选数据
列表、字典、集合生成式
过滤列表中的负数
取出字典中的值大于90的键值对
取出集合中能被3整除的数
△方案一: 列表解析、字典解析、集合解析
△方案二: filter
from random import randint
data_list = [randint(-10, 10) for _ in range(10)]
data_dict = {f'student{i}': randint(60, 100) for i in range(1, 20)}
data_set = {randint(0, 20) for _ in range(20)}
# -- 列表
[x for x in data_list if x >= 0]
filter(lambda x: x >= 0, data_list)
# -- 字典
{k: v for k, v in data_dict.items() if v > 90}
filter(lambda x: x[1] > 90, data_dict.items())
# -- 集合
{x for x in data_set if x % 3 == 0}
filter(lambda x: x % 3 == 0, data_set)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 元组元素命名
如何 为元组中的每个元素命名 ,提高程序的可读性?
对于格式固定的数据, 我们通常可以使用元祖来存储
比如某个学生信息系统, 其中每个学生的格式都是确定的(名字、年龄、性别、邮箱)
('Jim',16,'male',[email protected]
)1> 使用元祖的 优点 : 节省空间!!! 存储相同的数据, 使用元祖比使用字典开销会小很多!
2> 使用元祖的 缺点 : 访问的时候, 我们需要使用索引(index)访问, 大量索引降低程序可读性.
因为它不知道元组中每个字段的含义.
使用 collections.namedtuple
替代内置tuple
(还可以定义一系列数值常量或枚举类型来解决..但不推荐 略)
使用标准库collections下的一个命名元祖namedtuple: 即为元祖的每个字段起名字
它是一个类的工厂, 可以创建出一种元祖的子类, 它其中的每个字段是有名字的
第一个参数: 为命名元祖起个名字
第二个参数: 可以传入一个列表, 包含了命名元祖中每个字段的名字
return: 返回一个类, 元祖的子类
In [27]: from collections import namedtuple
# -- namedtuple()可以看作是一个类; student0是namedtuple()()的实例化,
In [28]: student0 = namedtuple('Stu0',['name','age','sex','email'])('xiaoming',
...: 16,'man','[email protected]')
In [29]: student0
Out[29]: Stu0(name='xiaoming', age=16, sex='man', email='[email protected]')
In [30]: student0[0]
Out[30]: 'xiaoming'
In [38]: student0.name
Out[38]: 'xiaoming'
2
3
4
5
6
7
8
9
10
11
12
13
# 字典排序
根据字典中值的大小对字典中的项进行排序,并加上排名
{'c': (1, 95), 'd': (2, 91)}
传递sorted函数的 key
参数.
from random import randint
data = {i: randint(60, 100) for i in 'abcdefg'}
temp = sorted(data.items(), key=lambda item: item[1], reverse=True)
# -- 等同于 {items[0]: (index, items[1]) for index, items in enumerate(temp, 1)}
res = {k: (index, v) for index, (k, v) in enumerate(temp, 1)}
print(res)
# -- 在原字典上,加上排名
for index, (k, v) in enumerate(temp, 1):
data[k] = (index, v)
"""
Ps: sorted(zip(data.values(), data.keys()))
"""
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 元素频度
统计序列中元素的频度并排序
1.某随机序列[12,5,6,4,6,5,5,7]中, 找到出现次数最高的3个元素, 它们出现的次数是多少?
2.对于某英文文章的单词, 进行词频统计, 找到出现次数最高的10个单词, 它们出现次数是多少?
△方案一: 将序列转换为字典 {元素:频度}
,根据字典中的值排序
△方案二: 使用标准库 collections
中的 Counter对象
Ps: 方案一,若列表很大只找前三个,对整个列表排序很浪费, 这时候可以使用堆 import heapq
"""
★ --方案一
"""
from random import randint
data = [randint(0, 20) for _ in range(30)]
# -- 对data数据去重后作为字典的键,每个键的值都默认为0
d = dict.fromkeys(data, 0)
# -- 注意一点 字典的键是唯一的 所以要达到预定效果就不能对d进行循环.
for i in data:
d[i] += 1
sorted(d.items(), key=lambda item: item[1], reverse=True)[:3]
# -- 改变键值顺序,通过元祖进行比较 好别扭
# sorted([(v, k) for k, v in d.items()], reverse=True)[:3]
sorted(((v, k) for k, v in d.items()), reverse=True)[:3]
"""
★ --方案二
"""
from collections import Counter
from random import randint
data = [randint(0, 20) for _ in range(30)]
# -- Counter就相当于一个频度字典
# -- 类似于这样 => Counter({7: 2,0: 1,10: 1,4: 1,16: 2,...,9: 2}
c = Counter(data)
# -- 取出频度最高的前3个数字
res = c.most_common(3)
print(res) # -- [(0, 6), (20, 3), (4, 3)]
"""
★ --文本词频统计
"""
import re
from collections import Counter
# -- 读取文件
txt = open('example.txt').read()
# -- 使用正则将词全部切分出来,可以得到一个词的列表
word_list = re.split('\W+', txt)
c2 = Counter(word_list)
# -- 取出文本中出现频度前10的单词
c2.most_common(10)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# 字典公共键
快速找到多个字典中的公共键
实际案例:
西班牙足球甲级联赛, 每轮球员进球统计:
第一轮: {'苏亚雷斯': 1, '梅西': 2, '本泽马':1,...}
第二轮: {'苏亚雷斯': 2, 'C罗': 2, '格里兹曼': 2,…}
第三轮: {'苏亚雷斯': 1, '托雷斯': 2, '贝尔': 1,…}
…… .... ....
统计出前N轮, 每场比赛都有进球的球员
△方案一: 可以使用循环
△方案二: map
(得到T or F)+all
+列表推导式
△方案三: 对于大量的字典, 我们通常利用集合的 交集
操作获取字典的公共键
from random import randint, sample
# -- 假设每轮有3~6人进球,每个人进球的数目1~4个
d1 = {k: randint(1, 4) for k in sample('abcdefgh', randint(3, 6))}
d2 = {k: randint(1, 4) for k in sample('abcdefgh', randint(3, 6))}
d3 = {k: randint(1, 4) for k in sample('abcdefgh', randint(3, 6))}
"""
★ --方法一:用for循环 看d1字典中的项是否在其余两个字典中
缺点: 面对实际问题,我们并不知道一共会有多少轮
"""
[k for k in d1 if k in d2 and k in d3]
"""
★ --方法二:map+all
直接对字典循环,循环打印出的是字典的键
可以通过 'd' in {'d': 2, 'a': 2} 这种方式判断字典中是否有这个键
`all([True,1]) => True`; `all([True,0]) => False`
"""
# -- [k for k in d_list[0] if all(map(lambda item:k in item,d_list[1:]))]
d_list = [d1, d2, d3]
my_list = []
for k in d_list[0]:
# -- all()函数用来判断可迭代参数iterable中的所有元素是否都为TRUE,如果是返回TRUE,否则返回False
if all(map(lambda item: k in item, d_list[1:])):
my_list.append(k)
print(my_list) # -- ['e', 'h', 'g']
"""
★ --方法三:map+reduce
Step1:使用字典的keys()方法,得到一个字典keys的集合
Step2:使用map函数,得到每个字典keys的集合
Step3:使用reduce函数,取所有字典的keys集合的交集
Ps: `d1.keys()` 等同于 `dict.keys(d1)`
In [16]: list(map(dict.keys,d_list))
Out[16]:
[dict_keys(['f', 'b', 'h', 'g', 'a', 'c']),
dict_keys(['g', 'f', 'a']),
dict_keys(['f', 'h', 'g', 'c', 'a', 'e'])]
"""
from functools import reduce
d_list = [d1, d2, d3]
res = reduce(lambda a, b: a & b, map(dict.keys, d_list))
print(res) # -- {'h', 'g', 'e'}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# 字典无序
解决python3.6以前字典无序的问题
使用标准库collections
中的OrderedDict
. 以OrderedDict替代内置字典Dict. 用法与字典一样!!
from collections import OrderedDict
from random import shuffle
players = list('abcdefgh')
shuffle(players) # 打乱players -- ['f', 'e', 'g', 'd', 'b', 'c', 'h', 'a']
od = OrderedDict()
for i, p in enumerate(players, 1):
od[p] = i
# -- 写一个接口,根据名字查询排名
def query_by_name(d, name):
return d[name]
query_by_name(od, 'c') # 6
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 可迭代对象切片
提一嘴, 切片的实质是重载了
__getitem__
方法
使用标准库 itertools
中的 islice
.
注意哦, 使用islice对字典切片,切的是key值.. islice返回的是一个迭代器
from itertools import islice
from collections import Iterable, Iterator ,Generator
# -- islice(iterable,start,stop[,step])
"""
★ --islice返回的是一个迭代器
"""
print(list(range(10)[3:6])) # [3, 4, 5]
train = islice(range(10), 3, 6)
# -- <class 'itertools.islice'> [3, 4, 5]
print(type(train), list(train))
# -- False True True
print(isinstance(train,Generator),isinstance(train,Iterable),isinstance(train,Iterator))
"""
★ --使用islice对字典切片,切的是key值
"""
my_dict = dict.fromkeys([i for i in 'abcde'], 0)
print(my_dict) # -- {'a': 0, 'b': 0, 'c': 0, 'd': 0, 'e': 0}
print(list(islice(my_dict, 3))) # -- ['a', 'b', 'c']
print(list(islice(my_dict, 1, 3))) # -- ['b', 'c']
"""
★ --使用islice对文件切片
"""
# -- 这个日志文件一共有70行
f = open('/var/log/wifi.log')
# -- 若通过`f.readlines()[10:20]`获取到行的列表,再对此进行切片,可行
# 但readlines()会先将文件所有内容读取到内存中,如果日志文件很大,就很不友好.
f.readlines()[10:20]
# -- islice在这里实际上读了20行 前10行扔掉
for line in islice(f, 10, 20):
print(line)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# 历史记录功能
需求: 制作一个简单的猜数字的小游戏, 添加历史记录功能, 显示用户猜过的数字..
1> 使用标准库 collections
中的 deque
,它是一个双端队列.
双端队列, 左右两端都可以进行出队和入队操作的队列;
(通常历史记录是有限度的,不能无限存储),使用容量为n的队列存储历史记录
2> 使用pickle
模块将历史记录存到硬盘,以便下次启动使用
因为这个队列是在内存当中的,程序退出后,历史记录就消失了.
from collections import deque
# -- 第一个参数: 对队列进行初始化
# 第二个参数: 队列的容量,不传默认队列无限大
q = deque([], 5)
# -- 入队操作有两个方法 q.append右端入队;q.appendleft左端入队
# -- 出队操作有两个方法 q.pop右端出队;q.popleft左端出队
q.append(1)
q.append(2)
q.append(3)
q.append(4)
q.append(5)
print(q) # deque([1, 2, 3, 4, 5], maxlen=5)
q.append(6)
# -- 容量满了,会将1自动弹出去
print(q) # deque([2, 3, 4, 5, 6], maxlen=5)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
猜数字实现:
from random import randint
from collections import deque
def guess(n, k):
# n是系统随机数,k是用户猜的数字
if n == k:
print('猜对了,这个数字是%d.' % k)
return True
if n < k:
print('猜大了,比%d小.' % k)
elif n > k:
print('猜小了,比%d大.' % k)
return False
def main():
n = randint(1, 100)
i = 1
hq = deque([], 5)
while True:
line = input('[%d] 请输入一个数字:' % i)
# -- isdigit()判断输入的是否是数字
if line.isdigit():
k = int(line)
hq.append(k)
i += 1
# -- 猜对了guess()返回True,程序退出;猜错了,继续猜.
if guess(n, k):
break
elif line == 'quit':
break
# -- 添加一个查询历史记录的接口
elif line == 'h?':
print(list(hq))
if __name__ == '__main__':
main()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
永久存储
# -- 使用pickle模块将历史记录存到硬盘,以便下次启动使用
import pickle
# -- dump()可以将一个python对象导入到文件中去
# 第一个参数:python对象
# 第二个参数:文件对象
# 注意存取操作都必须是二进制
pickle.dump(q, open('save.pkl', 'wb'))
# -- 取出
q2 = pickle.load(open('save.pkl', 'rb'))
print(q) # -- q: deque([2, 3, 4, 5, 6], maxlen=5)
print(q2) # -- q2: deque([2, 3, 4, 5, 6], maxlen=5)
2
3
4
5
6
7
8
9
10
11
# 字符串处理
# 拆分字符串
有这样一字符串, 'ab;cd|efg|hi,jkl|mn\topq;rst,uvw\txyz', 如何根据分隔符 ; , | \t 进行拆分?
△方案一: 连续使用str.split()
方法,每次只能处理一种分隔号, 需降维处理
map()+str.split()
sum()+str.split()
reduce()+map()+sum()+str.split()
△方案二: 使用正则表达式的 re.split()
方法
PS: 处理一个分隔符的时候使用方法一(一个的时候,方法二会比一慢);多个分隔符的时候使用方法二
"""
★ --方案一
说实话,有点复杂啦,但推导后加深了我对map、reduce、sum这些高阶函数的理解!!!这1个半小时没白花!
"""
▲ 理论验证 map()+str.split() sum()+str.split()
>>> s = 'ab;cd|efg|hi,jkl|mn\topq;rst,uvw\txyz'
# -- [['ab'], ['cd', 'efg', 'hi,jkl', 'mn\topq'], ['rst,uvw\txyz']]
>>> temp = [i.split('|') for i in s.split(';')]
>>> t = []
# -- list(map(lambda x: t.extend(x),temp))
>>> list(map(t.extend,temp))
[None, None, None] # -- 注意,t.extend的返回值为None
>>> t
['ab', 'cd', 'efg', 'hi,jkl', 'mn\topq', 'rst,uvw\txyz']
>>> sum(temp,[]) # -- 相当于 []+['ab', 'cd']+['efg'] ==> ['ab', 'cd', 'efg']
['ab', 'cd', 'efg', 'hi,jkl', 'mn\topq', 'rst,uvw\txyz']
# -- 说实话,效率战且不谈,挺难一眼看出逻辑的
# -- 这里注意个细节,`res = [s]`,首先将字符串放进了一个列表里
# 是为了后面map()映射函数,作用的是列表里的整个字符串,而不是字符串里的每一个字符
s = 'ab;cd|efg|hi,jkl|mn\topq;rst,uvw\txyz'
def my_split(s, seps):
res = [s]
"""
for循环可以改写为:
for sep in seps:
res = sum(map(lambda ss: ss.split(sep), res), [])
"""
for sep in seps:
t = []
# 举个例子: res=['ab,cd;efg'] map取到里面的元素'ab,cd;efg'赋值给ss
# 'ab,cd;efg'.split(',')得到结果['ab','cd;efg']
# t.extend(['ab','cd;efg']) 并将t赋值给res
# 同理,['ab','cd;efg']经过map加工,会依次执行
# t.extend('ab'.split(';')) t.extend('cd;efg'.split(';'))
# 再将t赋值给res,以此类推
list(map(lambda ss: t.extend(ss.split(sep)), res))
res = t
return res
print(my_split(s, ',;|\t'))
▲ 理论验证 reduce()+map()+sum()+str.split()
纯粹复杂化了!一点都不Effective ╮( ̄▽ ̄"")╭ 客官,图个乐吧
# -- reduce函数, 将字符串放进列表作为其匿名func的data参数的初始值, sep参数先得到值','
# -- [使用map函数取元素进行分割,分割会生成列表,再使用sum函数取列表元素操作..]
# <保证是在对元素级别进行操作> 因为map是迭代器,所以会一边map一边sum
# -- 将得到的结果再给匿名func的参数data, sep参数获得新的值';'
from functools import reduce
# 举个例子:data的初始值为['ab,cd;efg'] 将其用map进行加工
# map是一个迭代器,取data中的元素'ab,cd;efg'开始分割,
# 注意此元素是不可变对象,对其加工不会对原data造成任何影响!!!!
# split返回的是一个列表['ab','cd;efg'],sum函数对其进行操作 []+['ab','cd;efg']
# 将结果扔/赋值给reduce的data,此时data为['ab','cd;efg']
# 同样的用map对其进行加工,一边加工一边sum,即依次执行
# []+['ab'] ['ab']+['cd','efg'] 将最后结果['ab','cd',efg']扔给reduce的data
reduce(lambda data, sep: sum(map(lambda ss: ss.split(sep), data), []), ',;|\t',[s])
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
"""
★ --方案二
"""
import re
s = 'ab;cd|efg|hi,jkl|mn\topq;rst,uvw\txyz'
# + 代表一个或者多个
res = re.split('[;,|\t]+', s)
# ['ab', 'cd', 'efg', 'hi', 'jkl', 'mn', 'opq', 'rst', 'uvw', 'xyz']
print(res)
2
3
4
5
6
7
8
9
10
# 调整文本格式
某log日志文件. 其中的日期格式为'yyyy-mm-dd'
我们想把其中的日期改为美国日期的格式 'mm/dd/yyyy'
比如: '2016-05-23' ---> '05/23/2016', 应如何处理?
使用正则表达式re.sub()
方法做字符串替换,利用正则表达式的捕获组,捕获每个部分内容,
再替换字符串中调整各个捕获组的顺序
# -- 1.txt
2021-10-14 17:56:11 good good study 2021-09-09 day day up
2021-10-14 17:56:11 believe yourself
import re
with open('1.txt') as f:
log = f.read()
# -- 使用正则表达式的组()括起我们要提取的三个部分
# 捕获组\2 \3 \1 需要转译
print(re.sub('(\d{4})-(\d{2})-(\d{2})', r'\2/\3/\1', log))
# -- 可以给组命名
re.sub('(?P<y>\d{4})-(?P<m>\d{2})-(?P<d>\d{2})', r'\g<m>/\g<d>/\g<y>', log)
"""结果如下:
10/14/2021 17:56:11 good good study 09/09/2021 day day up
10/14/2021 17:56:11 believe yourself
"""
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 拼接字符串
在程序中我们将各个参数按次序收集到列表中:
['<0112>','<32>','<1024x768>','<60>','<1>','<100.0>','<500.0>']
最终我们要把各个参数拼接成一个数据报进行发送.
"<0112><32><1024x768><60><1><100.0><500.0>"
△方案一: 迭代列表,连续使用'+'操作依次拼接每一个字符串 reduce也能办到
缺点: 过程中创建了大量的字符串,空间时间上有很大浪费
△方案二: 使用str.join()
方法, 更加快速的拼接列表中所有的字符串 ✔!!!
它会一次性的创建内存,一次性的拷贝
"""
★ --方案一
缺点:过程中创建了大量的字符串,空间时间上有很大浪费
"""
▲ 理论验证
>>> s1 = 'abcdef'
>>> s2 = '12345'
>>> s1+s2
'abcdef12345'
>>> str.__add__
<slot wrapper '__add__' of 'str' objects>
>>> s1.__add__(s2)
'abcdef12345'
>>> str.__add__(s1,s2)
'abcdef12345'
my_list = ['<0112>', '<32>', '<1024x768>', '<60>', '<1>', '<100.0>', '<500.0>']
result = ""
for item in my_list:
result += item
# <0112><32><1024x768><60><1><100.0><500.0>
print(result)
# -- 简写
from functools import reduce
# <0112><32><1024x768><60><1><100.0><500.0>
print(reduce(str.__add__, my_list))
"""
★ --方案二
"""
>>> ''.join(['<0112>','<32>','<1024x768>','<60>','<1>','<100.0>','<500.0>'])
'<0112><32><1024x768><60><1><100.0><500.0>'
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 字符串对齐
某个字典存储了一系列属性值
{
"lodDist":100.0,
"SmallCull":0.04,
"DistCull":500.0,
"trilinear":40,
"farclip":477,
}
在程序中,我们想以工整的格式将其内容输出, 如何处理?
SmallCull:0.04,
farclip :477,
lodDist :100.0,
DistCull :500.0,
trilinear:40,
首先max + map
算出len(key)的最大值 便于对齐.
△方案一: 使用字符串的str.ljust()
, str.rjust()
, str.center()
进行左,右,居中对齐
△方案二: 使用format()
方法,传递类似'<20','>20','^20'参数完成同样任务
d = {
"lodDist": 100.0,
"SmallCull": 0.04,
"DistCull": 500.0,
"trilinear": 40,
"farclip": 477,
}
w = max(map(len, d.keys()))
for k, v in d.items():
print(k.ljust(w), ':', v)
'''
lodDist : 100.0
SmallCull : 0.04
DistCull : 500.0
trilinear : 40
farclip : 477
'''
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 去除字符
1.过滤掉用户输入中前后多余的空白字符 2.过滤某windows下编辑文本中的'\r'(window下的换行符是\r\n;linux下只需要一个\n) 'hello world\r\n'
△方法一: 字符串strip()
, lstrip()
, rstrip()
方法去掉字符串两端多种字符
△方法二: 删除单个固定位置的字符,可以使用切片然后拼接的方式
△方法三: 字符串中的替换方法replace()方法或正则表达式的re.sub()替换方法,替换成空字符串
△方法四: 字符串的 translate() 方法-它可以将一种字符映射到另外一种字符上,达到同时删除多种不同字符
>>> s3 = ' abc xyz '
>>> s3.replace(' ','')
'abcxyz'
>>> s3 = ' \t abc \t xyz \n '
>>> s3.replace(' ','').replace('\t','').replace('\n','')
'abcxyz'
>>> import re
>>> re.sub('[ \t\n]+','',s3)
'abcxyz'
>>> re.sub('\s+','',s3)
'abcxyz'
"""
★ --
translate() 允许传入一个映射表,根据映射表做替换
映射表的形式可以是一个字典 {unicode值:unicode值或字符串}
maketrans() 方便我们做映射表
"""
>>> s = 'abc1234xyz'
>>> s.translate({ord('a'):'X'})
'Xbc1234xyz'
>>> s.translate({ord('a'):'X',ord('b'):'Y'})
'XYc1234xyz'
>>> s.maketrans('abcxyz','XYZABC')
{97: 88, 98: 89, 99: 90, 120: 65, 121: 66, 122: 67}
>>> s.translate(s.maketrans('abcxyz','XYZABC'))
'XYZ1234ABC'
# -- 只要键值对的值为none就可以删除掉它!!!
>>> s.translate({ord('a'):None})
'bc1234xyz'
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 迭代与反迭代
# 抓取城市气温
某软件要求, 从网络抓取各个城市气温信息, 并依次显示:
北京: 15~20
天津: 17~22
长春: 12~18
... ... ...
如果依次抓取所有城市气温再显示, 显示第一个城市气温时, 有很高的延时, 并且浪费存储空间.
我们期望以"用时访问"的策略, 能把所有城市气温封装到一个对象里, 可用for语句进行迭代.
△方案一: 实现一个迭代器对象 , __next__
方法每次返回一个城市的信息
△方案二: 实现一个可迭代对象 , __iter__
方法返回一个生成器对象
# -- 可迭代对象和迭代器对象的基类. 我们可以不用继承它们.自己用魔法方法构建.
from collections import Iterable, Iterator
"""
★ --方案一
"""
import requests
class WeaterIterator():
def __init__(self, citys):
self.citys = citys
# 用于手工维护整个迭代器的状态
self.index = 0
def __iter__(self):
return self
def __next__(self):
if self.index == len(self.citys):
raise StopIteration
city = self.citys[self.index]
self.index += 1
return self.get_weather(city)
def get_weather(self, city):
url = "http://www.jcznedu.com:5000/weather/now/?city=" + city
r = requests.get(url)
# -- r.json()返回结果的json对象
data = r.json()["data"]["now"]
# -- 城市、温度、湿度
return city, data['temperature'], data['humidity']
def show(w):
for x in w:
print(x)
if __name__ == "__main__":
w = WeaterIterator(['北京', '上海', '广州'])
print(w.__next__())
print(w.__iter__())
show(w)
# ('北京', 28.3, 39.0)
# <__main__.WeaterIterator object at 0x7f9e9ce8eaf0>
# ('上海', 20.2, 99.0)
# ('广州', 33.0, 53.0)
"""
★ --方案二
"""
import requests
class Weater():
def __init__(self, citys):
self.citys = citys
def __iter__(self):
for city in self.citys:
yield self.get_weather(city)
def get_weather(self, city):
url = "http://www.jcznedu.com:5000/weather/now/?city=" + city
r = requests.get(url)
data = r.json()["data"]["now"]
return city, data['temperature'], data['humidity']
if __name__ == "__main__":
w = Weater(['北京', '上海', '广州'])
# -- for循环的时候,会调用w对象重写的__iter__方法,里面有yield关键字
# w.__iter__()得到一个迭代器 for循环再不断__next__取值
# print(w.__iter__().__next__())
# print(type(w.__iter__())) # <class 'generator'>
for x in w:
print(x)
# ('北京', 28.5, 39.0)
# ('上海', 20.2, 100.0)
# ('广州', 33.1, 51.0)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# 查找素数
实现一个可迭代对象的类,它能迭代出给定范围内所有素数:
质数即素数.指在大于1的自然数中,只能被1和自身整除的自然数pn = PrimeNumbers(1,30)
print([k for k in pn])
输出结果:
[2,3,5,7,11,13,17,19,23,29]
△方案一: for循环得到迭代器对象
△方案二: for循环得到生成器对象
"""
★ --方案一
"""
class PrimeNumbers:
def __init__(self, a, b):
self.a = a
self.b = b
def __iter__(self):
return self
def __next__(self):
if self.a < self.b + 1:
if self.is_prime(self.a):
index = self.a
self.a += 1
return index
self.a += 1
else:
raise StopIteration
def is_prime(self, k):
# -- 5%2 5%3 5%4 都有余数,证明5就是质数
return False if k < 2 else all(map(lambda x: k % x, range(2, k - 1)))
pn = PrimeNumbers(1, 30)
for n in pn:
if n != None:
print(n, end=' ')
"""
★ --方案二
"""
from inspect import isgenerator
class PrimeNumbers:
def __init__(self, a, b):
self.a = a
self.b = b
def __iter__(self):
for k in range(self.a, self.b + 1):
if self.is_prime(k):
yield k
def is_prime(self, k):
# -- 不会有浪费, all碰到一个假就会返回False
return False if k < 2 else all(map(lambda x: k % x, range(2, k - 1)))
pn = PrimeNumbers(1, 30)
# -- 这里它变成了生成器 `yield k` k是next()函数的返回值.
print(isgenerator(iter(pn))) # True
for n in pn:
print(n, end=' ') # -- 2 3 5 7 11 13 17 19 23 29
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# 浮点数发生器
实现一个连续浮点数发生器FloatRange(和range类似),
根据给定的范围(start,end)和步进值(step)产生一系列连续浮点数,
如迭代FloatRange(3.0,4.0,0.2) 可产生序列:正向: 3.0 -> 3.2 -> 3.4 -> 3.6 -> 3.8 -> 4.0
反向: 4.0 -> 3.8 -> 3.6 -> 3.4 -> 3.2 -> 3.0
解决方案: 实现反向迭代协议的 __reversed__
方法,它返回一个反向迭代器.
reversed()方法的本质是序列的底层实现了__reversed__
方法
>>> a = [1,2,3]
>>> reversed(a)
<list_reverseiterator object at 0x7ff5b973f2b0> # -- 得到一个反向迭代器
>>> reversed(a).__next__()
3
>>> iter(a)
<list_iterator object at 0x7ff5b97a4700> # -- 得到一个正向迭代器
2
3
4
5
6
7
8
浮点数的误差
>>> from decimal import Decimal
>>> Decimal('0.1')*3
Decimal('0.3')
>>> 0.1*3
0.30000000000000004
>>> Decimal('0.2')+Decimal('0.3') # -- 有些浮点数存在误差,为了避免,先把浮点数转换为字符串
Decimal('0.5')
>>> float(Decimal('0.2')+Decimal('0.3'))
0.5
# -- 2.675保留两位小数四舍五入结果是2.67而不是2.68,因为2.675在内部的存储值是2.647999999..
>>> round(2.675,2)
2.67
2
3
4
5
6
7
8
9
10
11
12
连续浮点数发生器代码如下:
from decimal import Decimal # -- py标准库中专门处理十进制浮点数的库
class FloatRange:
def __init__(self, a, b, step):
# -- 使用实例属性维护传入的这几个参数
self.a = Decimal(str(a))
self.b = Decimal(str(b))
self.step = Decimal(str(step))
def __iter__(self):
t = self.a
while t <= self.b:
yield float(t)
t += self.step
def __reversed__(self):
t = self.b
while t >= self.a:
yield float(t)
t -= self.step
fr = FloatRange(3.0, 4.0, 0.2)
for x in fr:
print(x)
print('-' * 10)
for x in reversed(fr):
print(x)
"""
3.0
3.2
3.4
3.6
3.8
4.0
----------
4.0
3.8
3.6
3.4
3.2
3.0
"""
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# 并行与串行
在for语句中迭代多个可迭代对象
(并行) 某班学生期末考试成绩,语文、数学、英语分别存储在三个列表中
同时迭代三个列表,计算每个学生的总分.
(串行) 某年级有4个班,某次考试每班英语成绩分别存储在4个列表中
依次迭代每个列表统计全学年成绩高于90分人数.
并行: 使用内置函数zip,它能将多个迭代对象合并,每次迭代返回一个元祖.
串行: 使用标准库中的itertools.chain
,它能将多个可迭代对象连接.
"""
★ --并行
"""
▲ 数据准备
from random import randint
chinese = [randint(60, 100) for _ in range(20)]
math = [randint(60, 100) for _ in range(20)]
english = [randint(60, 100) for _ in range(20)]
▲ 四种方式皆可
t1 = []
for s1, s2, s3 in zip(chinese, math, english):
t1.append(s1 + s2 + s3)
t2 = [sum(s) for s in zip(chinese, math, english)]
t3 = list(map(sum, zip(chinese, math, english)))
t4 = list(map(lambda s1, s2, s3: s1 + s2 + s3, chinese, math, english))
# -- Ps: map实现zip函数的效果
# list(map(lambda *args: args, chinese, math, english))
# list(zip(chinese, math, english))
"""
★ --串行
"""
from itertools import chain
from random import randint
for x in chain([1, 2, 3], [4, 5], [6, 7]):
print(x,end=" ") # -- 1 2 3 4 5 6 7
c1 = [randint(60, 100) for _ in range(20)]
c2 = [randint(60, 100) for _ in range(22)]
c3 = [randint(60, 100) for _ in range(25)]
len([x for x in chain(c1, c2, c3) if x > 90])
"""
★ --体验下*拆包与chain的作用
OS:说实话,这种技巧.. -_- 无语子.
"""
>>> from itertools import chain
>>> from functools import reduce
>>> s = 'abc;123|xyz;678|fweuow\tjzka'
>>> s.split(';')
['abc', '123|xyz', '678|fweuow\tjzka']
>>> list(map(lambda ss:ss.split('|'),s.split(';')))
[['abc'], ['123', 'xyz'], ['678', 'fweuow\tjzka']]
>>> list(chain(*[['abc'], ['123', 'xyz'], ['678', 'fweuow\tjzka']]))
['abc', '123', 'xyz', '678', 'fweuow\tjzka']
# -- reduce(lambda data, sep: sum(map(lambda ss: ss.split(sep), data), []), ',;|\t',[s])
>>> list(reduce(lambda it_s,sep:chain(*map(lambda ss:ss.split(sep),it_s)),';|\t',[s]))
['abc', '123', 'xyz', '678', 'fweuow', 'jzka']
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55