Python 函数:从“是什么”到“怎么用”的完整指南

函数是 Python 世界里的第一等公民。掌握它,就掌握了把复杂度关进笼子里的钥匙。


一、为什么需要函数

  1. 复用:写一次,用 N 次。

  2. 抽象:把"怎么做"隐藏,只暴露"做什么"。

  3. 测试:最小可测单元,方便单元测试。

  4. 组合:乐高式地拼出更大的功能。

二、函数的本质:可调用的对象

在 Python 里,一切皆对象,函数也不例外。

python 复制代码
>>> def add(a, b): return a + b
>>> type(add)
<class 'function'>
>>> add.__call__(3, 4)   # 函数对象内部实现了 __call__
7

因此函数可以:

  • 赋值给变量

  • 放进列表 / 字典

  • 作为参数或返回值

三、定义函数:语法全景图

python 复制代码
def 函数名([参数列表]) -> 返回注解:
    """可选的文档字符串"""
    函数体
    [return 表达式]
  • 函数名 :遵循蛇形命名 snake_case,动词优先。

  • 冒号 + 缩进 代替 C/Java 的花括号。

  • return 可省略,此时隐式返回 None

  • 注解 只是元数据,可被 __annotations__ 查看,不做运行时检查。

四:参数类型全景

1.位置参数(Positional-only Arguments,最"老实"的参数)

这是最传统、最没有花样的参数形态:你在定义时写一个名字,调用时按先后顺序把值塞进去即可。它既不能跳过,也不能用关键字"点名"。例如 def power(base, exponent),调用时必须写 power(2, 3),不能写 power(base=2, exponent=3)------顺序一旦写错,结果就南辕北辙。位置参数的好处是直观、高效;坏处是当参数一多,可读性会骤降,因此 Python 3.8 引入了"仅限位置参数"的语法符号 /,把这一风格显式化:

python 复制代码
def tag(name, content, /):
    return f"<{name}>{content}</{name}>"

2.默认值参数(Default Argument Values,给参数一个"备胎")

有时某个参数在大多数场景里都是一个固定值,为了避免每次调用都重复书写,可以给它一个默认值。语法是在形参后加 = 和一个表达式。注意:表达式只在函数定义时求值一次,如果默认值是可变对象(列表、字典、集合),就可能踩到"共享陷阱"。典型写法:

python 复制代码
def connect(host, port=3306, timeout=5):
    ...

调用时可以省略已有默认值的参数:connect('db.example.com')connect('db.example.com', 5432)。默认值参数必须放在位置参数之后,否则解释器无法分辨谁是谁。

3.可变位置参数(Var-Positional,*args)

当你不确定调用者会传多少个位置参数,或者想让它"想传几个就传几个"时,可以在形参名前加一个星号 *。Python 会把所有多余的位置实参打包成一个元组,绑到这个形参上。函数内部可以像遍历普通元组一样遍历 args

python 复制代码
def mean(*values):
    return sum(values) / len(values) if values else 0.0

调用示例:mean(1, 2, 3, 4)mean() 都合法。它带来的好处是接口极致简洁,坏处是静态类型检查工具(如 mypy)无法推断每个元素的具体类型,需要额外标注 Tuple[int, ...]

4.可变关键字参数(Var-Keyword,**kwargs)

*args 类似,但收集的是"关键字实参"。在形参名前加两个星号 **,Python 会把所有未匹配的关键字实参打包成一个字典。典型场景是"透传"参数,如写装饰器时把上层收到的不确定关键字参数全部交给被装饰函数:

python 复制代码
def trace(func, *args, **kwargs):
    print(f"Calling {func.__name__} with {args} and {kwargs}")
    return func(*args, **kwargs)

调用示例:trace(open, 'file.txt', mode='wb', buffering=0)。字典键都是字符串,值可以是任意对象。

5.仅限关键字参数(Keyword-Only Arguments,用星号"隔出"的参数)

有时候你希望强制调用者用关键字形式传参,以提升可读性或防止顺序写错。做法是在参数列表里放一个裸星号 *,它之后的所有参数都必须用关键字传递。

python 复制代码
def create_user(username, *, is_admin=False, send_welcome=True):
    ...

调用时必须写 create_user('alice', is_admin=True),而不能写 create_user('alice', True)。这一特性在写配置类 API 时尤为有用,能避免"布尔陷阱"。

6.仅限位置参数

与第 5 点相反,/ 之前的参数禁止 用关键字形式传递。其目的在于让库作者未来能自由重命名这些参数而不破坏向后兼容。例如 CPython 内置函数 divmod(a, b) 就把两个参数都设为仅限位置,所以我们只能写 divmod(10, 3),而不能写 divmod(a=10, b=3)

7.解包调用

虽然它发生在调用端而非定义端,但经常与上述参数类型一起出现,因此一并说明。你可以在调用函数时使用 * 把序列/可迭代对象解包成位置实参,用 ** 把字典解包成关键字实参。

python 复制代码
coords = (4, 5)
point = {'x': 10, 'y': 20}
move_to(*coords)          # 等价于 move_to(4, 5)
create_point(**point)     # 等价于 create_point(x=10, y=20)

当序列长度与形参数量不匹配时会抛出 TypeError,需要保证一一对应。

五、参数传递机制:共享传参(Call by Object Reference)

  • Python 没有真正的"值传递"或"引用传递"。

  • 形参 = 实参对象的 新引用

  • 若对象可变,原地修改 对外部可见;若不可变,重新绑定不影响外部。

python 复制代码
def append_one(xs):
    xs.append(1)          # 列表可变,外部可见
def set_zero(x):
    x = 0                 # 整数不可变,外部无感

lst = []
append_one(lst); print(lst)  # [1]
n = 42
set_zero(n); print(n)        # 42

六、高级话题速览

  • 高阶函数

    map / filter / functools.reduce / sorted(key=...)

  • 闭包与延迟绑定陷阱

python 复制代码
fs = [lambda: i for i in range(3)]
print([f() for f in fs])   # [2, 2, 2]
# 修复:lambda i=i: i
  • 装饰器

    本质是"函数返回函数",加上 @ 语法糖。

  • 单分派泛函数

    @functools.singledispatch 实现 C++ 风格重载。

  • 类型提示与静态检查

python 复制代码
from typing import List, Callable
def pipeline(data: List[int], fn: Callable[[int], int]) -> List[int]: ...

七、最佳实践清单

  • 单一职责:一个函数只做一件事。

  • 早返回(return early)减少嵌套。

  • 使用 dataclassesNamedTuple 替代裸字典返回。

  • 文档字符串格式:"""Do X and return Y.""" 或 Google / NumPy 风格。

  • 对可变默认值使用 None 占位:

python 复制代码
def append(item, seq=None):
    if seq is None:
        seq = []

八、小结

函数是 Python 抽象、组合、复用 的核心。

从最简单的 def 到装饰器、闭包、类型提示,每一步都让代码更声明式 、更可测试

相关推荐
神仙别闹2 小时前
基于 C++ 实现(控制台)学生成绩管理系统
开发语言·c++
Y幽谷客2 小时前
Python加载本地大模型(Qwen3.5 8B)
python·大模型
伞伞悦读2 小时前
【第36期】Python 目录与路径详解:pathlib、文件遍历、创建、复制、移动和删除风险
开发语言·python
线上放牧人3 小时前
Windows删除图标缓存
windows·python·pyqt
qq_5470261793 小时前
Python 变量和简单数据类型
python
Koi慢热3 小时前
DayDayMap学术社区体验:卫星网络测绘专题用下来怎么样
网络·人工智能·windows·web安全·网络安全
web守墓人4 小时前
【goed/ui】自定义组件设计思想篇
linux·windows·ui·golang
智搜广告4 小时前
智搜广告:科技行业AI回答优化公司如何破局
大数据·python·elasticsearch·geo
IpdataCloud5 小时前
AI智能体调用工具怎么核验来源IP?归属地、网络类型与代理风险识别(含Python代码)
数据库·python·tcp/ip
2601_966949655 小时前
只拿到股票代码还不够:量化程序到底还需要哪些标的信息?——从数据建模开始理解股票元数据
开发语言·python·数据分析·pandas·量化交易·股票数据·quantdash