第 5 章 函数

5.1 定义与调用

def greet(name):
    """向指定的人问好。(这是 docstring 文档字符串)"""
    return f"Hello, {name}!"

greet("Alice")       # 'Hello, Alice!'
help(greet)          # 显示 docstring
  • def 定义,函数名约定 snake_case;

  • return 返回结果并结束函数;没有 return 或裸 return 返回 None;

  • return a, b 实际返回一个元组,可解包接收;

  • 函数是一等对象:可以赋值给变量、当参数传递、放进列表/字典。

5.2 参数详解(重点)

位置参数与关键字参数

def intro(name, age):
    print(f"{name},{age} 岁")

intro("Alice", 25)              # 位置参数(按顺序)
intro(age=25, name="Alice")     # 关键字参数(按名,可乱序)
intro("Alice", age=25)          # 混用:位置参数必须在前面

默认参数

def power(x, n=2):
    return x ** n

power(3)        # 9
power(3, 3)     # 27

黄金规则:默认值必须是不可变对象(None、数字、字符串等)。可变默认值的坑见第 3 章 3.6 节——默认参数在函数定义时只求值一次,之后所有调用共享。

可变参数:*args 与 **kwargs

def demo(a, *args, **kwargs):
    print(a)        # 1
    print(args)     # (2, 3)   多余位置参数,打包成元组
    print(kwargs)   # {'x': 4} 多余关键字参数,打包成字典

demo(1, 2, 3, x=4)

调用端的 * / ** 是解包:

nums = [1, 2, 3]
print(*nums)                    # 1 2 3
d = {"name": "Alice", "age": 25}
intro(**d)                      # 等价 intro(name="Alice", age=25)

参数定义顺序与强制规则

完整顺序:位置参数 → 默认参数 → *args → 仅关键字参数 → **kwargs

def f(pos, default=1, *args, kw_only, **kwargs): ...

# 仅位置参数(/)与仅关键字参数(*)分隔符,3.8+:
def create_user(name, /, *, password):
    ...

create_user("alice", password="123")   # ✅
create_user(name="alice", password="123")  # ❌ name 只能按位置传

5.3 作用域:LEGB 规则

变量查找顺序:Local(函数内)→ Enclosing(外层函数)→ Global(模块级)→ Built-in(内置)。

x = "global"

def outer():
    x = "enclosing"
    def inner():
        x = "local"
        print(x)        # local
    inner()

def counter():
    count = 0
    def inc():
        nonlocal count  # 声明修改外层函数变量
        count += 1
        return count
    return inc

c = counter()
c(); c()      # 1, 2 —— 闭包记住了 count
  • 函数内读取全局变量没问题;赋值会默认创建局部变量,除非声明 global x;

  • 修改外层函数变量用 nonlocal;

  • 滥用 global 是坏味道,优先通过参数传入、返回值传出。

5.4 匿名函数 lambda

add = lambda x, y: x + y        # 单表达式,隐式 return

lambda 的真正用武之地是高阶函数的实参:

pairs = [(1, "one"), (4, "four"), (2, "two")]
sorted(pairs, key=lambda p: p[1])       # 按元组第二项排序

users = [{"name": "B", "age": 20}, {"name": "A", "age": 30}]
max(users, key=lambda u: u["age"])

list(map(lambda x: x * 2, [1, 2, 3]))   # [2, 4, 6]
list(filter(lambda x: x > 1, [1, 2, 3]))  # [2, 3]

风格建议:能写 def 就别赋值 lambda(PEP 8);简单场景优先用推导式替代 map/filter。

5.5 常用内置高阶函数

nums = [3, 1, 4, 1, 5]
sorted(nums, reverse=True)         # 排序(返回新列表)
sum(nums); max(nums); min(nums)
list(map(str, nums))               # 对每个元素应用函数
list(filter(None, [0, 1, "", "a"]))  # [1, 'a'] 过滤真值
any(x > 3 for x in nums)           # True,存在性判断
all(x > 0 for x in nums)           # False,全称判断

5.6 类型提示(Type Hints)

Python 是动态类型,但可以标注类型,提升可读性并支持工具检查(mypy / IDE):

def repeat(text: str, times: int = 2) -> str:
    return text * times

def process(items: list[str]) -> dict[str, int]:   # 3.9+ 泛型内建写法
    return {item: len(item) for item in items}

# 运行时不强制!类型不符不会报错,只是给工具看的契约
repeat(3, "x")    # 运行时返回 333,但 mypy 会警告

常见标注:int、float、str、bool、list[X]、dict[K, V]、X | None(3.10+ 表示可空)。

5.7 装饰器(入门)

装饰器是"接收函数、返回函数"的高阶函数,用 @ 语法给函数包一层:

import time
import functools

def timer(func):
    @functools.wraps(func)          # 保留原函数的名称和 docstring
    def wrapper(*args, **kwargs):
        start = time.perf_counter()
        result = func(*args, **kwargs)
        print(f"{func.__name__} 耗时 {time.perf_counter() - start:.4f}s")
        return result
    return wrapper

@timer
def slow_sum():
    return sum(range(10_000_000))

slow_sum()     # 自动打印耗时

等价于 slow_sum = timer(slow_sum)。常见内置/标准库装饰器:@staticmethod、@classmethod、@property(见第 6 章)、@functools.cache。

5.8 本章小结

  • 参数规则:默认值用不可变对象;*args/**kwargs 收集与解包是一对;

  • 作用域按 LEGB 查找;改外层变量需要 nonlocal/global;

  • lambda 只做单表达式;类型提示是给人看的契约;

  • 装饰器 = 函数包函数,@functools.wraps 别忘加。

下一章:面向对象——类、继承与魔术方法。

Python3 基础知识 / 第 5 章 函数 0 字 0 行 cosolar
2026-09-17T11:10:47.703265041Z 2026-09-17T11:25:54.187470475Z