Python 从零开始 · 第 11 课:进阶语法与函数式编程
基础语法你已经学完了。这一课我们点几个"高手技能点"——它们不是必须的,但掌握后你会:看懂别人代码、写出更简洁优雅的代码。
核心三件套:生成器/迭代器、装饰器、函数式工具(map/filter/推导式)。
一、迭代器:能被"挨个取出"的东西
我们早就用过迭代了——for 循环能遍历列表、字典、集合、字符串,因为它们都是可迭代对象。
for x in [1, 2, 3]: # 列表可迭代
print(x)背后靠的是迭代器协议:对象提供 __iter__ 和 __next__,就能被 for 一个个取。我们用 iter() 和 next() 手动看看:
it = iter([1, 2, 3])
print(next(it)) # 1
print(next(it)) # 2
print(next(it)) # 3
print(next(it)) # StopIteration:取完了二、生成器:边算边给,省内存
生成器(generator)是一种特殊的迭代器,用 yield 而不是 return。它不一次性算出所有结果,而是用到哪、算到哪:
def 平方生成器(n):
for i in range(n):
yield i * i # yield:给一个,暂停,下次接着算
g = 平方生成器(1000000) # 不会立刻算 100 万个
print(next(g)) # 0
print(next(g)) # 1
print(next(g)) # 4对比一下:[i*i for i in range(1000000)] 会立刻在内存里建一个 100 万元素的列表;而生成器版本的 (i*i for i in range(1000000)) 只算用到的。
生成器 = 省内存的懒计算。 处理超大/无限数据时尤其有用:
def 无限自然数():
n = 0
while True:
yield n
n += 1
it = 无限自然数()
print(next(it)) # 0
print(next(it)) # 1三、生成器表达式:一行版生成器
列表推导式是 [],生成器表达式是 ():
squares_list = [x*x for x in range(10)] # 列表,立刻算好
squares_gen = (x*x for x in range(10)) # 生成器,懒计算
print(sum(squares_gen)) # 285,sum 会触发它逐个算四、装饰器:给函数"套壳"
装饰器(decorator)能在不修改原函数的情况下,给它增加额外功能。比如想给函数计时:
import time
def 计时器(func): # 装饰器:接受一个函数
def wrapper(*args, **kwargs): # 包一层新函数
start = time.time()
result = func(*args, **kwargs)
print(f"耗时 {time.time() - start:.4f} 秒")
return result
return wrapper # 返回包装后的函数
@计时器 # 用 @ 挂上装饰器
def 慢函数():
time.sleep(1)
慢函数() # 打印:耗时 1.0000 秒@计时器 等价于 慢函数 = 计时器(慢函数)——把原函数包一层再赋回去。
装饰器的典型用途:计时、日志、权限校验、缓存。Flask/Django 这些框架里满屏都是 @app.route(...)。
五、函数式工具:map / filter
函数式编程喜欢"把函数作用到数据上"。Python 提供了:
nums = [1, 2, 3, 4, 5]
# map:对每个元素应用函数
list(map(lambda x: x * 2, nums)) # [2, 4, 6, 8, 10]
# filter:按条件过滤
list(filter(lambda x: x % 2 == 0, nums)) # [2, 4]
# reduce:累积(在 functools 里)
from functools import reduce
reduce(lambda a, b: a + b, nums) # 15,累加不过 Python 圈更推荐列表推导式,比 map/filter 更直白:
[x * 2 for x in nums] # 等价 map [x for x in nums if x % 2 == 0] # 等价 filter
六、args 和 *kwargs:接收任意参数
装饰器里出现的 *args 和 **kwargs,作用是"接收任意数量的参数":
def 打印所有(*args):
for a in args:
print(a)
打印所有(1, 2, 3, "hello") # 打包成一个元组
def 打印字典(**kwargs):
for k, v in kwargs.items():
print(k, v)
打印字典(name="张三", age=20) # 打包成一个字典*args:位置参数打包成元组。**kwargs:关键字参数打包成字典。
七、动手时间 🎯
实验 1:斐波那契生成器
def fib(n):
a, b = 0, 1
for _ in range(n):
yield a
a, b = b, a + b
for x in fib(10):
print(x, end=" ")
# 0 1 1 2 3 5 8 13 21 34实验 2:给函数加"日志"装饰器
def 日志(func):
def wrapper(*args, **kwargs):
print(f"调用 {func.__name__},参数 {args}")
result = func(*args, **kwargs)
print(f"返回 {result}")
return result
return wrapper
@日志
def add(a, b):
return a + b
add(3, 5)
# 调用 add,参数 (3, 5)
# 返回 8实验 3:处理大文件用生成器
def 逐行读(path):
with open(path, "r", encoding="utf-8") as f:
for line in f:
yield line.strip()
for line in 逐行读("data.txt"):
print(line)八、小结
- 生成器用
yield,边算边给、省内存;生成器表达式(...)是懒计算版的列表推导式。 - 装饰器用
@给函数套壳,不改原函数就加功能(计时/日志/缓存),*args/**kwargs接收任意参数。 - map/filter 能实现函数式,但列表推导式往往更直白——优先用推导式。
下一课,我们学 常用工具库 numpy 与 pandas——这是进入数据分析、机器学习世界的门票,几乎所有 ML 代码都离不开它们。
先把「斐波那契生成器」和「日志装饰器」敲熟,进阶语法的手感就长在你脑子里了。