基础语法你已经学完了。这一课我们点几个"高手技能点"——它们不是必须的,但掌握后你会:看懂别人代码、写出更简洁优雅的代码

核心三件套:生成器/迭代器装饰器函数式工具(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)

八、小结

  1. 生成器用 yield,边算边给、省内存;生成器表达式 (...) 是懒计算版的列表推导式。
  2. 装饰器用 @ 给函数套壳,不改原函数就加功能(计时/日志/缓存),*args/**kwargs 接收任意参数。
  3. map/filter 能实现函数式,但列表推导式往往更直白——优先用推导式。

下一课,我们学 常用工具库 numpy 与 pandas——这是进入数据分析、机器学习世界的门票,几乎所有 ML 代码都离不开它们。

先把「斐波那契生成器」和「日志装饰器」敲熟,进阶语法的手感就长在你脑子里了。

标签: none

添加新评论