智泊Python--Day10

智泊Python–Day10

  Day10 是函数主题的收尾,并引出 Python 的「惰性计算」体系。先讲递归(函数自己调用自己,靠临界值收口),再讲列表 / 字典 / 集合推导式(一行 for 生成容器),然后重点是生成器和迭代器——这两者都「用到才生成」,能省内存;最后落到工程层面:第三方包怎么装、包 / 模块 怎么组织与导入。记住一句话:生成器 ⊂ 迭代器 ⊂ 可迭代对象。

 

1.函数递归

  递归两大要素:找递推公式 + 设临界值(出口)。阶乘 n! = n * (n-1)!,临界值是 n==1;斐波那契 fib(n)=fib(n-1)+fib(n-2),临界值是 n==1 或 2。下面那个「兔子繁衍」是变体:当月对数 = 上月对数 + 三个月前的新生对数,即 f(n)=f(n-1)+f(n-3),临界 n<=3 返回 1。

1
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
# 函数递归:再函数内部调用自己

# 找公式
# 设置临界值

# 需求 求5的阶乘
def fn(n):
if n == 1:
return 1
else:
return n * fn(n-1)

print(fn(5))

# 封住函数,存入m 得到第m个斐波那契数列的值
def fib(n):
if n == 1 or n == 2:
return 1
else:
s = fib(n-1) + fib(n-2)
return s
print(fib(5))

# 兔子繁衍问题,设有一对新生的兔子,从第4个月开始他们每个月月初都生一对新兔子,
# 新生的兔子从第4个月开始又每个月月初生一对兔子。按此规律,并假定兔子没有死亡,
# 20个月末共有多少对兔子

# 公式
# 当月兔子的对 = 上月兔子的对f(n-1) + 上月新生的对f(n-3)
# 新生的对 = 前三月有的兔子的对f(n-3)

def f(n):
if n <= 3:
return 1
else:
s = f(n-1) + f(n-3)
return s

print(f(20))

 

2.列表推导式

  推导式是「用一行 for 生成容器」的语法糖,比先建空列表再 append 更简洁。列表用 []、字典用 {} 配 键:值、集合用 {}。可以加 if 过滤,也支持多层 for 嵌套。

1
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
# 列表推导式(掌握)
# 使用for循环用一行来生成列表

# 创建一个列表
nums = [1, 2, 3, 4, 5]
print(nums)
print("="*20)
nums1 = []
for i in range(1, 6):
nums1.append(i)
i += 1
print(nums1)
print("="*20)
# 列表推导式
nums2 = [i for i in range(1, 6)]
print(nums2)
nums3 = [i*2 for i in range(1, 6)] # 2倍
print(nums3)
nums4 = [i for i in range(1, 6) if i%2 == 1] # 奇数
print(nums4)
nums5 = [i for i in range(1, 6) if i%2 == 1 and i != 3] # 奇数且不包含3
print(nums5)

# if嵌套
nums6 = [i for i in range(1, 7) if i%2==0 if i%3 == 0] # 偶数且是3的倍数
print(nums6)

# for循环嵌套
nums7 = [i+j for i in range(1, 6) for j in range(1, 6)]
print(nums7)
print("="*20)

# 字典推导式(用大括号 {})(了解)
dict1 = {f'key{i}': i*2 for i in range(1, 6)}
print(dict1)

# 集合推导式(用大括号 {})(了解)
set1 = {i for i in range(1, 6)}
print(set1)

 

3.生成器和生成器函数

  生成器用 () 定义(区别于列表的 []),不一次性算出所有值,而是用到才生成,所以只占一点内存。生成器函数则在函数体内用 yield:每调一次 next(),执行到 yield 就暂停并返回值,下次再从暂停处继续——return 会结束函数,yield 只是暂停。下面用 while True: yield a; a,b=b,a+b 优雅地实现了无限斐波那契序列。

1
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
# 生成器 generator
# 需要使用next或者for循环来调用

# 使用的是小括号 () 来定义生成器,大括号 {} 来定义字典,中括号 [] 来定义列表组
gen = (i for i in range(1, 6)) # 生成器
print(gen) # <generator object <genexpr> at 0x0000024D0000> 生成器对象

# for i in gen:
# print(i)

# 只能next一次,不能重复next
for i in range(1, 6):
print(next(gen))


#生成器函数
# 函数内部要有yield
# 需要用next函数来调用
# 每个next函数都会在yield语句中暂停
# yield会暂停 ,可以返回值
def gen_fn():
print('我是gen_fn, 你看我会执行吗'),
yield 666 # 会暂停,并返回值,默认返回None,与return None效果相同但是不会结束函数
print('我是gen_fn, 你看我会执行吗2'),
yield 777 # 会暂停,并返回值,默认返回None,与return None效果相同但是不会结束函数

gen1 = gen_fn()
print(gen1) # <generator object gen_fn at 0x0000024D0000>
print(next(gen1)) # 666
print(next(gen1)) # 777


#实例
def gen_fn2():
g = (i for i in range(1, 10**100))
for i in g:
yield i

gen2 = gen_fn2()
print(gen2) # <generator object gen_fn2 at 0x0000024D0000>
print(next(gen2)) # 1
print(next(gen2)) # 2
print(next(gen2)) # 3


#练习
#1.请写一个生成器函数,得到前20个斐波那契数(难度:*****)
# 斐波那契数列如下:0,1,1,2,3,5,8,13,21,34,55...
# 提示:使用whileTrue,通过调用n次next来获取前20个数

print("="*20)
def fib_fn():
a, b = 0, 1
while True:
yield a
a, b = b, a+b

fib = fib_fn()
for i in range(20):
print(next(fib))

 

4.迭代器

  三者的关系用一句话记:可迭代对象(能放 for 里遍历)⊃ 迭代器(记住遍历位置,能 next)⊃ 生成器(yield 写的迭代器)。注意 list/str/dict/set/生成器都是可迭代对象,但它们本身不是迭代器(isinstance(..., Iterator) 为 False);只有生成器表达式 (i for ...) 才是迭代器。iter() 能把可迭代对象转成迭代器。下面已删除两行与主题无关的自动补全残留导入。

1
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
# isinstance() 函数
# 用于判断一个对象是否是某个类的实例
print(10, type(10)) # <class 'int'>
print(isinstance(10, int)) # True
print(isinstance(10, (int ,float,list))) # 判断类型是否为整数、浮点数、列表中的一个
print("="*30)

# 迭代器Iterator 是指一个可以记住遍历位置的对象,每次调用next()方法时,返回下一个值
from typing import Iterator

print(isinstance([1,2,3], Iterator)) # False
print(isinstance({1,2,3}, Iterator)) # False
print(isinstance((1,2,3), Iterator)) # False
print(isinstance((i for i in range(10)), Iterator)) # True
print("="*30)


# 可迭代对象Iterable 是指一个可以返回迭代器的对象
# 有 __iter__():凡是能放进 for x in … 的东西
# 例:list / str / dict / set / 文件 / 生成器
from typing import Iterable
print(isinstance(3, Iterable)) # False
print(isinstance(3.14, Iterable)) # False
print(isinstance("hello", Iterable)) # True
print(isinstance([1,2,3], Iterable)) # True
print(isinstance({1,2,3}, Iterable)) # True
print(isinstance((1,2,3), Iterable)) # True
print(isinstance((i for i in range(10)), Iterable)) # True

# iter() 函数
# 用于将一个可迭代对象转换为迭代器
print(iter([1,2,3])) # <list_iterator object at 0x0000022222222222>
print(iter("hello")) # <str_iterator object at 0x0000022222222222>
print(iter({1,2,3})) # <dict_iterator object at 0x000x000002222


# 本质上 生成器 ⊂ 迭代器 ⊂ 可迭代对象
# 可迭代对象是一个装着数据的东西,可以被遍历
# 迭代器是一个翻看工具,它记住看到第几个了
# 生成器是迭代器的懒人写法(用 yield 关键字)

 

5.第三方包的安装

  Python 的包分三类:官方内置、自己写的、别人写的第三方包。日常用 pip 管理第三方包,常用命令都列在下面。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 1. Python内部提供的包或模块(官方)
# 2.我们直接写的包或模块(自己的)
# 3.别人写的 第三方包或模块(非官方)


# 第三方包的安装
# 1. PyCharm中python解释器配置软件包的功能
# 2. 先导入包名,鼠标悬停查看包的文档
# 3. 命令安装
# pip install numpy 安装包
# pip uninstall numpy 卸载包
# pip show numpy 查看包的详细信息
# pip list 查看所有安装的包
# pip freeze 查看所安装的包的版本号
# pip freeze > requirements.txt 导出所有安装的包的版本号到文件
# pip install -r requirements.txt 从文件中安装所有包

 

6.包和模块

  组织代码的层次是:项目 → 包(带 __init__.py 的文件夹)→ 模块(.py 文件)→ 类 → 函数。导入有几种姿势:import 模块、from 模块 import 名字、from 模块 import *、as 起别名(起名后只能用别名)。模块被导入时会自动执行一次其中的顶层代码,且是单例——多次导入只执行一遍。

1
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
# 包 package: 是一个有__init__.py文件的文件夹
# 模块 module 是一个.py文件

# 封装思路: 项目 ==》 包 ==》 模块(python文件) ==》 类 (class) ==》函数

# 创建包
# 创建模块

# 导入模块
# import 模块名
# from 模块名 import 函数名
# from 模块名 import *

#自定义模块
# 1. 创建一个.py文件
# 2. 在文件中定义函数
# 3. 导出函数
# 4. 导入模块
# 5. 调用函数

# import package1

# from package1 import module
# # 直接打印ikun 是因为导入的时候就会自动执行模块中的代码
# # 并且模块是单例模式,只会执行一次
# print(module.name)
# print(module.fn())
from package1.module import name
print(name)


# 别名:as 改名后只能用别名
# from package1 import module as m1
import package1.module as m1

 

7.作业

  两道大作业:①万年历——综合运用 Day8 的闰年 / 月天数判断,从 1900-01-01 算总天数再推算当月 1 号是星期几,格式化打印整月日历(依赖自定义模块 package1.wnl);②写一个无限序列生成器,从 1 开始每次 +1,用 while True + yield 实现,外层 next() 取前 n 个(原笔记里那行 numpy.f2py 导入与本题无关,已删除)。

1
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
#1.实现万年历效果图,使用函数封装:
#
# A:先输出提示语句,并接受用户输入的年、月。
# B:根据用户输入的年,先判断是否是闰年。
# C:根据用户输入的月来判断月的天数。
# D:用循环计算用户输入的年份距1900年1月1日的总天数。
# E:用循环计算用户输入的月份距输入的年份的1月1日共有多少天,
# F:相加D与E的天数,得到总天数。
# G:用总天数来计算输入月的第一天的星期数
# H:根据G的值,格式化输出这个月的日历!

# from package1 import wnl

# wnl.FA()


#2.请写一个生成器函数,生成一个无限序列,从1开始可以不断往后取值,每次+1
# 提示:使用whileTrue,通过调用n次next来获取前n个数

def infinite_sequence():
i = 1
while True:
yield i
i += 1


G2 = infinite_sequence()

import time
while True:
print(next(G2))
time.sleep(1)

智泊Python--Day10
https://one-null-pointer.github.io/2026/07/29/智泊Python--Day10/
Author
liaoyue
Posted on
July 29, 2026
传送口