Skip to content

📦 函数与模块 ​

数据有了,流程有了,但代码一长就像一锅粥。函数和模块就是把粥分碗装——各司其职,互不打扰。

函数定义与调用 ​

用 def 关键字定义函数:

python
def greet(name):
    """向某人打招呼(这是文档字符串)"""
    return f"你好,{name}!"

# 调用
print(greet("小雅"))  # 你好,小雅!

文档字符串

函数体内第一行用 """...""" 写的字符串就是文档,之后用 help(greet) 或 greet.__doc__ 可以查看。养成写文档的好习惯。

参数类型 ​

Python 的参数系统非常灵活:

python
# 1. 位置参数 — 按顺序传
def add(a, b):
    return a + b
print(add(3, 5))  # 8

# 2. 默认参数 — 不传就用默认值
def greet(name, greeting="你好"):
    return f"{greeting},{name}!"
print(greet("小雅"))           # 你好,小雅!
print(greet("Tom", "Hello"))  # Hello,Tom!

# 3. 关键字参数 — 按名字传,顺序无所谓
print(greet(greeting="Hi", name="Jack"))  # Hi,Jack!

# 4. 可变参数 *args — 接收任意多个位置参数(打包成元组)
def sum_all(*nums):
    return sum(nums)
print(sum_all(1, 2, 3, 4, 5))  # 15

# 5. 关键字可变参数 **kwargs — 接收任意多个关键字参数(打包成字典)
def print_info(**info):
    for key, value in info.items():
        print(f"{key}: {value}")
print_info(name="小雅", age=25, city="杭州")
# name: 小雅
# age: 25
# city: 杭州

组合使用时的顺序:**位置参数 → *args → 关键字参数 → kwargs。

返回值 ​

python
# 无 return → 返回 None
def do_nothing():
    pass
print(do_nothing())  # None

# 返回多个值 → 其实是返回元组
def get_min_max(nums):
    return min(nums), max(nums)

a, b = get_min_max([3, 1, 4, 1, 5])  # a=1, b=5

作用域 — LEGB 规则 ​

Python 找变量按 LEGB 顺序:Local → Enclosing → Global → Built-in。

python
x = "global"        # 全局变量

def outer():
    x = "enclosing"  # 闭包变量
    def inner():
        x = "local"  # 局部变量
        print(x)     # 先找局部 → "local"
    inner()

outer()
  • global:在函数内声明要修改全局变量
  • nonlocal:在内层函数声明要修改外层(闭包)变量
python
count = 0

def increment():
    global count
    count += 1

def make_counter():
    n = 0
    def counter():
        nonlocal n
        n += 1
        return n
    return counter

lambda — 匿名函数 ​

短小的一次性函数,一条表达式搞定:

python
# 传统写法
def square(x):
    return x**2

# lambda 写法
square = lambda x: x**2

# 常用于排序、过滤、map
students = [("小明", 85), ("小红", 92), ("小刚", 78)]
students.sort(key=lambda s: s[1])  # 按分数排序

nums = [1, 2, 3, 4, 5]
evens = list(filter(lambda x: x % 2 == 0, nums))  # [2, 4]
doubled = list(map(lambda x: x * 2, nums))        # [2, 4, 6, 8, 10]

模块 — 代码分文件 ​

创建模块 ​

一个 .py 文件就是一个模块。创建 utils.py:

python
# utils.py
def compress_file(path):
    """压缩文件"""
    import zipfile
    with zipfile.ZipFile(path + ".zip", "w") as zf:
        zf.write(path)
    print(f"{path} 已压缩")

def send_email(to, subject, body):
    """发送邮件(示例)"""
    print(f"→ 发送邮件到 {to}")
    print(f"  主题: {subject}")

导入模块 ​

python
import utils                         # 导入整个模块
utils.compress_file("data.txt")

from utils import send_email         # 导入特定函数
send_email("test@qq.com", "Hello", "正文")

from utils import compress_file as cf  # 起别名
cf("report.csv")

from utils import *                  # ❌ 不推荐:导入所有,污染命名空间

__name__ == "__main__" ​

python
# utils.py
if __name__ == "__main__":
    print("直接运行 utils.py 时执行这里")
    # 可以写测试代码

直接 python utils.py 时会执行;被 import 时不会执行。

实战:工具函数库 ​

python
# toolkit.py — 可复用的工具函数
import logging
from datetime import datetime

def setup_logger(name="app"):
    """快速创建日志器"""
    logging.basicConfig(
        level=logging.INFO,
        format="%(asctime)s [%(levelname)s] %(message)s"
    )
    return logging.getLogger(name)

def timeit(func):
    """装饰器:打印函数执行时间"""
    def wrapper(*args, **kwargs):
        start = datetime.now()
        result = func(*args, **kwargs)
        print(f"{func.__name__} 耗时: {datetime.now() - start}")
        return result
    return wrapper

@timeit
def slow_task():
    total = sum(range(10_000_000))
    return total

# 直接运行测试
if __name__ == "__main__":
    logger = setup_logger()
    logger.info("工具库加载成功")
    slow_task()

常见坑 ​

坑1:默认参数用可变对象

默认参数在函数定义时只计算一次,共享同一个对象:

python
# ❌ 错误写法
def add_item(item, items=[]):
    items.append(item)
    return items

print(add_item(1))  # [1]
print(add_item(2))  # [1, 2] ← 意外!列表被共享了

# ✅ 正确写法
def add_item(item, items=None):
    if items is None:
        items = []
    items.append(item)
    return items

坑2:循环导入

A 导 B,B 导 A → 死循环。解法:把共同的依赖抽到第三个模块,或把 import 放到函数内部。

坑3:from module import *

会导入模块中所有公开名字,容易覆盖已有变量。永远用显式导入。

练习题 ​

  1. 写一个 calculator(a, b, op) 函数,op 可以是 "+"、"-"、"*"、"/",默认 "+"
  2. 用 *args 写一个函数,返回所有参数的乘积
  3. 写一个装饰器,把函数返回值翻倍
  4. 把练习题 1~3 放到一个 myutils.py 模块里,在另一个文件中导入使用
  5. 用 __name__ == "__main__" 给你的模块写一段自测代码

📦 函数与模块让代码井井有条。下一步学 面向对象编程,用类的思维建模现实世界。


🎯 本章要点 ​

  • 默认参数在函数定义时计算一次。
  • *args 收集所有位置参数到一个元组,**kwargs 收集所有关键字参数到字典。
  • lambda 适合一行搞定的简单表达式
加载练习题中...