2025-09-10:仓库迁移
This commit is contained in:
24
4.迭代器与生成器/1.手动访问迭代器中的元素.py
Normal file
24
4.迭代器与生成器/1.手动访问迭代器中的元素.py
Normal file
@@ -0,0 +1,24 @@
|
||||
from scipy.stats import trim1
|
||||
|
||||
if __name__ == "__main__":
|
||||
items = [1, 2, 3]
|
||||
items = iter(items)
|
||||
# 如果需要访问可迭代对象中的元素,可以使用next函数
|
||||
while True:
|
||||
item = next(items, None)
|
||||
|
||||
if item is not None:
|
||||
print(item)
|
||||
else:
|
||||
break
|
||||
|
||||
# next函数的第一个参数是一个可迭代对象,第二个参数是None,是没有元素的时候的返回值
|
||||
|
||||
# 如果不限制迭代器在没有元素后的返回值,迭代器在迭代结束后会抛出一个StopIteration的错误
|
||||
while True:
|
||||
try:
|
||||
item = next(items)
|
||||
except StopIteration:
|
||||
print("没东西了")
|
||||
break
|
||||
|
13
4.迭代器与生成器/10.以键值对的方式迭代索引.py
Normal file
13
4.迭代器与生成器/10.以键值对的方式迭代索引.py
Normal file
@@ -0,0 +1,13 @@
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
my_list = [1,2,3,4,5,6,7,8,9]
|
||||
# 有时候,我们想要在迭代的时候知道元素的下标,有些傻子(比如我)就很傻逼的做了个计数器
|
||||
# 但是,其实直接使用enumerate就行
|
||||
for index, number in enumerate(my_list, start=0):
|
||||
print(index, number)
|
||||
|
||||
# 但是如果可迭代对象的子元素是元组,那请务必别把元组的标识拆开
|
||||
my_list = [(1, 2), (3, 4), (5, 6), (7, 8), (9, 10)]
|
||||
for index, (x, y) in enumerate(my_list, start=1):
|
||||
print(index, x, y)
|
30
4.迭代器与生成器/11.迭代多个序列.py
Normal file
30
4.迭代器与生成器/11.迭代多个序列.py
Normal file
@@ -0,0 +1,30 @@
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# 如果想要同时迭代多个序列,那么可以使用zip函数
|
||||
list1 = [1, 2, 3, 4, 5]
|
||||
list2 = ['a', 'b', 'c', 'd', 'e']
|
||||
|
||||
for i, j in zip(list1, list2):
|
||||
print(i, j)
|
||||
|
||||
# zip遵循最短木板原则,迭代长度等于最短的可迭代对象长度,如果想要打破这种限制,请使用itertools.zip_longest()
|
||||
|
||||
from itertools import zip_longest
|
||||
short_list = [1, 2, 3]
|
||||
# fillvalue字段用来填充短列表的缺失值,默认为None
|
||||
for i, j in zip_longest(short_list, list1, fillvalue=0):
|
||||
print(i, j)
|
||||
|
||||
# zip函数会构建一个元组,它的长度与输入的可迭代对象数量有关
|
||||
for i in zip(short_list, list2, list1):
|
||||
print(i)
|
||||
|
||||
# 双对象元组可以直接视作键值对转成字典
|
||||
dic = dict(zip(list1, list2))
|
||||
print(dic)
|
||||
|
||||
# 重点!!!zip函数返回的是一个一次性迭代器,如果需要长久保存,请转成列表在内存里持久化
|
||||
storage_list = list(zip(list1, list2))
|
||||
|
||||
|
12
4.迭代器与生成器/12.在不同的容器中迭代.py
Normal file
12
4.迭代器与生成器/12.在不同的容器中迭代.py
Normal file
@@ -0,0 +1,12 @@
|
||||
from itertools import chain
|
||||
|
||||
if __name__ == '__main__':
|
||||
a = [1,2,3,4,5]
|
||||
b = ['a','b','c','d','e']
|
||||
|
||||
# 正常来说,我们想要迭代这两个东西需要写两个循环构建两个迭代器,但这样很操蛋,不如直接用chain把他俩作链表连起来
|
||||
for i in chain(a, b):
|
||||
print(i)
|
||||
|
||||
# 使用chain可以避开 a + b 要求a和b类型相同的限制,同时这种操作更加快速,因为其底层是用指针完成,而不会创建一个新的东西
|
||||
|
41
4.迭代器与生成器/13.创建处理数据的管道.py
Normal file
41
4.迭代器与生成器/13.创建处理数据的管道.py
Normal file
@@ -0,0 +1,41 @@
|
||||
import os
|
||||
from fnmatch import fnmatchcase
|
||||
|
||||
if __name__ == "__main__":
|
||||
base_path = "D:\Code\Learn\practice"
|
||||
|
||||
# 想要建立一个处理管道,需要将几个生成器函数堆叠,比如,我现在要输出所有practice文件夹下的注释行内容
|
||||
# 1.首先生成文件夹路径
|
||||
def gen_dir_path(fpath):
|
||||
for path, dir, files in os.walk(fpath):
|
||||
for file in files:
|
||||
if (not fnmatchcase(path, "*.[git][idea]*")) and (not fnmatchcase(file, "*.md")):
|
||||
yield os.path.join(path, file)
|
||||
|
||||
# 2.打开文件
|
||||
def gen_file(paths):
|
||||
for path in paths:
|
||||
f = open(path, 'rt', encoding='utf-8')
|
||||
yield f
|
||||
f.close()
|
||||
|
||||
# 3.读取文件内容
|
||||
def gen_file_txt(opened_files):
|
||||
for file in opened_files:
|
||||
yield from file
|
||||
print("done")
|
||||
|
||||
# 4.匹配符合条件的文件行
|
||||
def read_file_lines(file_lines):
|
||||
for line in file_lines:
|
||||
if "#" in line:
|
||||
print(line)
|
||||
|
||||
# 将这些函数堆在一起就组成了一条管道,每次生成一个对象进行流水线处理,
|
||||
# 这样避免了for循环的嵌套
|
||||
paths = gen_dir_path(base_path)
|
||||
files = gen_file(paths)
|
||||
lines = gen_file_txt(files)
|
||||
read_file_lines(lines)
|
||||
|
||||
|
18
4.迭代器与生成器/14.扁平化处理嵌套型的序列.py
Normal file
18
4.迭代器与生成器/14.扁平化处理嵌套型的序列.py
Normal file
@@ -0,0 +1,18 @@
|
||||
from collections import Iterable
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
# 将复杂的序列扁平化,使用递归来脱壳
|
||||
def flatten(items, ignore=(str, bytes)):
|
||||
for item in items:
|
||||
# 要进行递归,首先这个元素要是可迭代的,其次它不能是字符串或二进制串
|
||||
if isinstance(item, Iterable) and not isinstance(item, ignore):
|
||||
# yield from 将请求转发到flatten函数返回的迭代器进行嵌套,避免再写for循环
|
||||
# yield from 在后面的协程和基于生成器的并发程序里有重要作用
|
||||
yield from flatten(item, ignore)
|
||||
else:
|
||||
yield item
|
||||
|
||||
a = [1, 2, [3, 4, [5, 6], 7], 8]
|
||||
for item in flatten(a):
|
||||
print(item)
|
17
4.迭代器与生成器/15.合并多个有序序列,再对整个有序序列进行迭代.py
Normal file
17
4.迭代器与生成器/15.合并多个有序序列,再对整个有序序列进行迭代.py
Normal file
@@ -0,0 +1,17 @@
|
||||
import heapq
|
||||
from itertools import chain
|
||||
|
||||
if __name__ == '__main__':
|
||||
a = [1, 4, 7, 10]
|
||||
b = [2, 5, 6, 11]
|
||||
|
||||
# 首先复习一下前面的知识,如果不考虑合并后也是有序的,用chain
|
||||
for item in chain(a, b):
|
||||
print(item)
|
||||
|
||||
# 但如果我们想要得到有序合并序列,那就要用堆自带的merge功能了,利用小根堆的特殊性来排序
|
||||
new_arr = heapq.merge(a, b)
|
||||
for item in new_arr:
|
||||
print(item)
|
||||
|
||||
# 记住,heapq的merge事先不会对输入可迭代对象的有序性进行检查,而是每次从可迭代对象序列里取出最小的第一个进堆
|
1
4.迭代器与生成器/16.test.txt
Normal file
1
4.迭代器与生成器/16.test.txt
Normal file
@@ -0,0 +1 @@
|
||||
Hello PythonCookBook
|
33
4.迭代器与生成器/16.用迭代器取代while循环.py
Normal file
33
4.迭代器与生成器/16.用迭代器取代while循环.py
Normal file
@@ -0,0 +1,33 @@
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
CHUNKSIZE = 10
|
||||
|
||||
def render(s):
|
||||
# 军火展示
|
||||
print(s.read())
|
||||
s.seek(0)
|
||||
|
||||
# 在处理文件的时候,我们习惯用while循环来迭代数据
|
||||
print("一般while处理:")
|
||||
while True:
|
||||
data = s.read(CHUNKSIZE)
|
||||
if data == b'':
|
||||
break
|
||||
print(data)
|
||||
|
||||
s.seek(0)
|
||||
|
||||
|
||||
# 但是可以使用迭代器来升级一下
|
||||
print("进化成迭代器:")
|
||||
for chunk in iter(lambda: s.read(CHUNKSIZE), b''):
|
||||
print(chunk)
|
||||
|
||||
path = "4.迭代器与生成器/16.test.txt"
|
||||
# 二进制打开文件记得用rb,下一章进化一下文件读取方面的知识
|
||||
f = open(path, 'rb')
|
||||
|
||||
render(f)
|
||||
print("Done")
|
||||
|
28
4.迭代器与生成器/2.委托迭代.py
Normal file
28
4.迭代器与生成器/2.委托迭代.py
Normal file
@@ -0,0 +1,28 @@
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
class Node:
|
||||
def __init__(self, value):
|
||||
self._value = value
|
||||
self._children = []
|
||||
|
||||
def __repr__(self):
|
||||
return 'Node({!r})'.format(self._value)
|
||||
|
||||
def add_child(self, node):
|
||||
self._children.append(node)
|
||||
|
||||
# 对象的内置iter方法,在迭代对象的时候将迭代请求转发,返回children的迭代器对象
|
||||
# iter函数和len函数一样,底层原理都是返回对象的底层方法,iter返回对象的__iter__()
|
||||
def __iter__(self):
|
||||
return iter(self._children)
|
||||
|
||||
root = Node(0)
|
||||
child1 = Node(1)
|
||||
child2 = Node(2)
|
||||
root.add_child(child1)
|
||||
root.add_child(child2)
|
||||
|
||||
for ch in root:
|
||||
print(ch)
|
||||
|
52
4.迭代器与生成器/3.使用生成器创建新的迭代模式.py
Normal file
52
4.迭代器与生成器/3.使用生成器创建新的迭代模式.py
Normal file
@@ -0,0 +1,52 @@
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
def fbb(start, stop, step):
|
||||
x = start
|
||||
while x <= stop:
|
||||
yield x
|
||||
x += step
|
||||
# 上面是一个生成器函数,它只会在相应迭代时运行
|
||||
|
||||
# 在调用生成器函数的时候,因为直接生成了数字,所以有返回值
|
||||
for i in fbb(0, 10, 1):
|
||||
print(i)
|
||||
|
||||
# 尝试使用fbb生成金字塔
|
||||
|
||||
# 先用生成器整一个斐波那契数列生成函数
|
||||
def fbb_arr(n):
|
||||
index = 0
|
||||
a0 = 1
|
||||
a1 = 1
|
||||
while index < n:
|
||||
yield a0
|
||||
a0, a1 = a1, a0 + a1
|
||||
|
||||
index += 1
|
||||
|
||||
# 按照n行打印出来
|
||||
def print_num_delta(n):
|
||||
# 计算需要生成多少个数字
|
||||
fbb_number_num = sum(range(1, n + 1))
|
||||
# 生成数字并保存
|
||||
fbb_list = list(fbb_arr(fbb_number_num))
|
||||
# 数字下标,用于遍历中保存位置
|
||||
start_index = 0
|
||||
|
||||
# 每一行的数字数量
|
||||
for number_num in range(1, n + 1):
|
||||
# 切片用空格分隔后转成字符串
|
||||
nums = ' '.join(str(item) for item in fbb_list[start_index: start_index + number_num])
|
||||
# 格式化打印
|
||||
print(format(nums, r' ^{}'.format(len(str(fbb_list[-1])) * n)))
|
||||
# 更新下一行的数字下标
|
||||
start_index = start_index + number_num
|
||||
|
||||
print_num_delta(5)
|
||||
|
||||
# 生成器里为什么没有return: 如果一个函数里包含yield语句,那就会被识别为生成器函数,
|
||||
# 函数里的return此时自动失效被替换成生成器
|
||||
# 生成器函数在识别到return时,意识到生成结束,返回的StopIteration,交给for识别
|
||||
|
31
4.迭代器与生成器/4.迭代协议.py
Normal file
31
4.迭代器与生成器/4.迭代协议.py
Normal file
@@ -0,0 +1,31 @@
|
||||
# 这是标准的
|
||||
class Node:
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
self._children = []
|
||||
|
||||
def __repr__(self):
|
||||
return "Node({})".format(self.value)
|
||||
|
||||
def add_child(self, node):
|
||||
self._children.append(node)
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self._children)
|
||||
|
||||
def depth_first(self):
|
||||
yield self
|
||||
for child in self:
|
||||
yield from child.depth_first()
|
||||
|
||||
if __name__ == '__main__':
|
||||
root = Node(0)
|
||||
child1 = Node(1)
|
||||
child2 = Node(2)
|
||||
root.add_child(child1)
|
||||
root.add_child(child2)
|
||||
child1.add_child(Node(3))
|
||||
child2.add_child(Node(4))
|
||||
|
||||
for ch in root.depth_first():
|
||||
print(ch)
|
31
4.迭代器与生成器/5.反向迭代.py
Normal file
31
4.迭代器与生成器/5.反向迭代.py
Normal file
@@ -0,0 +1,31 @@
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
a = [1, 2, 3, 4]
|
||||
# python自带的reversed函数搞定了反向迭代,其本质时调用了__reversed__方法
|
||||
for i in reversed(a):
|
||||
print(i)
|
||||
|
||||
for i in a.__reversed__():
|
||||
print(i)
|
||||
|
||||
# 如果想要实现类的反向迭代,可以在实现__iter__()方法的同时搞一个__reversed__()方法
|
||||
class Countdown:
|
||||
def __init__(self, start):
|
||||
self.start = start
|
||||
|
||||
def __iter__(self):
|
||||
n = self.start
|
||||
while n > 1:
|
||||
yield n
|
||||
n -= 1
|
||||
|
||||
def __reversed__(self):
|
||||
n = 1
|
||||
while n <= self.start:
|
||||
yield n
|
||||
n += 1
|
||||
|
||||
a = Countdown(10)
|
||||
for i in reversed(a):
|
||||
print(i)
|
38
4.迭代器与生成器/6.定义带有额外状态的生成器函数.py
Normal file
38
4.迭代器与生成器/6.定义带有额外状态的生成器函数.py
Normal file
@@ -0,0 +1,38 @@
|
||||
from collections import deque
|
||||
|
||||
if __name__ == "__main__":
|
||||
# 在学到了生成器函数以后,有一个巨大陷阱那就是尝试用生成器函数解决一切问题
|
||||
# 当生成器函数需要与外界交互的时候,就会让它非常复杂
|
||||
# 所以生成器我们就让它安心生成,在需要记录或者与外界交互的时候,写一个生成器类
|
||||
class FbbIterClass:
|
||||
def __init__(self,nums, history_len=3):
|
||||
self.nums = nums
|
||||
self.history = deque(maxlen=history_len)
|
||||
|
||||
def __iter__(self):
|
||||
a0, a1 = 1, 1
|
||||
index = 0
|
||||
while index < self.nums:
|
||||
self.history.append(a0)
|
||||
yield a0
|
||||
a0, a1 = a1, a0 + a1
|
||||
index += 1
|
||||
|
||||
fbbs = FbbIterClass(5)
|
||||
|
||||
# 先用生成器生成,结果在生成的过程中会存入历史队列
|
||||
for i in fbbs:
|
||||
print(i)
|
||||
|
||||
# 当然,在使用for之外的方法进行迭代时,需要额外套一层iter来转发请求到__iter__()
|
||||
fbb_iter = iter(fbbs)
|
||||
print(fbb_iter.__next__())
|
||||
print(fbb_iter.__next__())
|
||||
print(fbb_iter.__next__())
|
||||
print(fbb_iter.__next__())
|
||||
print(fbb_iter.__next__())
|
||||
|
||||
# 这种方法的好处是,可以在迭代时随时暴露一些东西给外部程序,比如属性和状态
|
||||
for line in fbbs.history:
|
||||
print("history{}".format(line))
|
||||
|
26
4.迭代器与生成器/7.对迭代器做切片操作.py
Normal file
26
4.迭代器与生成器/7.对迭代器做切片操作.py
Normal file
@@ -0,0 +1,26 @@
|
||||
from itertools import islice
|
||||
|
||||
if __name__ == '__main__':
|
||||
# 在程序生成过程中,我们有时候需要做一些无限生成器,这些生成器往往需要切片使用
|
||||
def infinity_iterator(start):
|
||||
while True:
|
||||
yield start
|
||||
start += 1
|
||||
|
||||
# 无限生成器
|
||||
for i in infinity_iterator(0):
|
||||
print(i)
|
||||
break
|
||||
|
||||
# 想要对无限生成器进行切片,我们就需要使用itertools里的islice函数,记得这个函数也是左闭右开
|
||||
iterator= infinity_iterator(0)
|
||||
iter_10_20 = islice(infinity_iterator(0), 10, 21)
|
||||
for i in iter_10_20:
|
||||
print(i)
|
||||
# 注意!!!islice操作是通过丢弃索引序列前的元素实现的,返回的是一个一次性迭代器,被迭代过后元素就被消耗掉无了
|
||||
print("再次使用迭代")
|
||||
for i in iter_10_20:
|
||||
print(i)
|
||||
|
||||
|
||||
|
22
4.迭代器与生成器/8.跳过可迭代对象中的前一部分元素.py
Normal file
22
4.迭代器与生成器/8.跳过可迭代对象中的前一部分元素.py
Normal file
@@ -0,0 +1,22 @@
|
||||
from itertools import dropwhile, islice
|
||||
|
||||
if __name__ == '__main__':
|
||||
words = ["#look", "#into", "#my", "eyes", "look", "into", "my", "eyes",
|
||||
"the", "eyes", "the", "eyes", "the", "eyes", "not", "around", "the",
|
||||
"eyes", "don't", "look", "around", "the", "eyes", "look", "into",
|
||||
"my", "eyes", "you're", "under"]
|
||||
|
||||
# 想要对可迭代对象进行过滤,也可以使用dropwhile函数,比如这里我想过滤掉前几行有#的东西
|
||||
for i in dropwhile(lambda x : x.startswith('#'), words):
|
||||
print(i)
|
||||
|
||||
# dropwhile会在迭代时对函数进行匹配,将符合条件的值丢弃,直到出现第一个符合条件的值后停止工作返回所有剩余内容
|
||||
|
||||
# 如果你知道需要跳过几个元素,那可以直接用islice进行切片操作来过滤
|
||||
|
||||
items = ['a', 'b', 'c', 1, 2, 3]
|
||||
# None表示[3:]
|
||||
for i in islice(items, 3, None):
|
||||
print(i)
|
||||
|
||||
# 当然,使用列表的过滤方法也可以删除一些不要的东西,但是这样的删除是全局的,而不是仅在开头生效
|
23
4.迭代器与生成器/9.迭代所有的排列组合.py
Normal file
23
4.迭代器与生成器/9.迭代所有的排列组合.py
Normal file
@@ -0,0 +1,23 @@
|
||||
from itertools import permutations, combinations
|
||||
|
||||
if __name__ == '__main__':
|
||||
# 有时候我们想对可迭代对象中的元素进行排列组合,这个时候我们就不得不靠for循环写出很不python的代码
|
||||
# itertools库帮助我们解决了这个困扰
|
||||
items= ['a', 'b', 'c', 'd', 'e']
|
||||
|
||||
# permutations会列出所有的排列,如果指定排列长度,那就会输出该长度下所有的排列
|
||||
for p in permutations(items):
|
||||
print(p)
|
||||
|
||||
for p in permutations(items, 3):
|
||||
print(p)
|
||||
|
||||
# 如果想要得到特定长度的组合,那就可以使用combination函数
|
||||
for c in combinations(items, 3):
|
||||
print(c)
|
||||
|
||||
# 上面这种组合是不放回的组合,如果想产生放回元素的组合,可以使用combinations_with_replacement函数
|
||||
from itertools import combinations_with_replacement as cwr
|
||||
for c in cwr(items, 3):
|
||||
print(c)
|
||||
|
Reference in New Issue
Block a user