Python函数-函数基础
一、函数基础
1、函数简介
- 函数是具有某种特定功能的代码块,可以重复使用
2、函数定义
def 函数名(参数1, 参数2=默认值, *args, **kwargs): """可选文档字符串""" 函数体 return 返回值二、函数参数概述
1、5 种参数类型速查
| 类型 | 关键字 | 语法示例 | 调用示例 | 说明 |
|---|---|---|---|---|
| 位置参数(positional-only) | 无 | def f(a, b, /): | f(1, 2) | 必须按位置,禁止关键字 |
| 位置或关键字 | 无 | def f(a, b): | f(1, 2) 或 f(a=1, b=2) | 默认形态 |
| 关键字参数(keyword-only) | *, | def f(*, k): | f(k=3) | 必须显式 k=... |
| 默认参数 | = | def f(a, b=0): | f(1) | 缺省值 |
| 可变位置 | *args | def f(*args): | f(1, 2, 3) | 多余位置收集成元组 |
| 可变关键字 | **kwargs | def f(**kwargs): | f(a=1, b=2) | 多余关键字收集成字典 |
2、4 阶顺序规则(PEP 570/PEP 3102)
def f(pos_only, /, pos_or_kwd, *, kwd_only, **extra): ... ↑ ↑ ↑ 位置限定 默认区 关键字限定/左侧:仅限位置*左侧:位置或关键字*右侧:仅限关键字**kwargs最后收集剩余关键字
三、函数参数类型
1、位置参数
1.1、简介
- 位置参数是最常见的函数形参类型,它要求调用者 按形参在定义中的顺序 依次传入实参。
- 在 没有
/或*限定符 时,位置参数也允许 关键字传参;一旦加了/,则强制“仅限位置”
1.2、语法规则
def 函数名(位置参数1, 位置参数2, ...): 函数体- 根据函数定义的参数位置来传递参数,要求传递的参数与函数定义的参数两者一一对应
- 如果 “传递的参数个数” 不等于 “函数定义的参数个数”,运行时会报错
# 错误栗子def add(a, b): return a + b
sum = add(1, 2, 3)
# 输出结果 sum = add(1, 2, 3)E TypeError: add() takes 2 positional arguments but 3 were given2、关键字参数
2.1、简介
- 关键字参数允许调用者 以“键=值”形式 传参,不再受形参顺序限制,提高可读性、容错性和可扩展性。可与位置参数混用,但需遵循 位置在前、关键字在后 的顺序。
2.2、语法规则
定义端:
- 默认形态:形参无特殊符号 → 支持位置或关键字
- 强制关键字:在形参列表中插入
*,或*, kw→ 其后形参 必须 用关键字传值
调用端:func(key=value) 或 func(pos, key=value)
顺序约束:位置实参 → *args → 关键字实参 → **kwargs
# 普通关键字(位置或关键字)def power(base, exp=2): return base ** exp
# 合法调用power(2, 3) # 位置power(base=2, exp=3) # 关键字power(exp=3, base=2) # 关键字顺序无关# 强制关键字(keyword-only)def connect(host, port, *, ssl=True, timeout=30): return f"{host}:{port} ssl={ssl} timeout={timeout}"
# 正确connect("api.io", 443, ssl=False, timeout=10)
# 错误connect("api.io", 443, False) # TypeError: missing keyword# 混用可变参数def log(msg, *tags, level="INFO", **extra): print(msg, tags, level, extra)
log("error", "tag1", "tag2", level="ERROR", user="bob")# 输出: error ('tag1', 'tag2') ERROR {'user': 'bob'}3、默认参数
3.1、简介
- 默认参数允许在 函数定义 时为形参指定一个 缺省值,调用者可选择省略该参数,从而简化调用并增强向后兼容性。
3.2、语法规则
def func(形参1, 形参2=默认值, 形参3=默认值2): ...- 默认值可以是任意 不可变或可变的 Python 对象。
- 默认值只在函数定义时求值一次,并在后续所有调用中共享(见常见陷阱)。
- 默认参数 必须排在非默认参数之后。
def greet(name, greeting="Hello"): return f"{greeting}, {name}!"
print(greet("Alice")) # Hello, Alice!print(greet("Bob", "Hi")) # Hi, Bob!3.3、调用规则
| 调用方式 | 结果 |
|---|---|
| 省略默认参数 | 使用默认值 |
| 显式传值 | 覆盖默认值 |
3.4、默认参数陷阱 —— 可变对象共享
def append(item, lst=[]): lst.append(item) return lst
print(append(1)) # [1]print(append(2)) # [1, 2] ← 共享同一列表
# 修复:None + 新建def append(item, lst=None): if lst is None: lst = [] lst.append(item) return lst4、可变位置
4.1、简介
- 可变位置参数允许函数接收 任意数量的“多余”位置实参,并将它们打包成一个 元组。常用于需要“不定数量输入”的场景,如求和、日志格式化、装饰器封装等。
4.2、语法规则
def func(pos1, pos2, *args, kw_only="default"): ...- 定义端:在形参名前加 单个星号 *,习惯命名为 *args(args 不是关键字,可改)。
- 位置约束:*args 必须位于 普通位置参数之后、默认参数之前(若有默认参数需放在 *args 之后,见 PEP 3102)。
- 调用端:多余的 位置实参 被 依次收集到元组。
4.3、常见错误
- 位置实参在前:
*args之后不能再出现 普通位置参数(会触发语法错误)。 - 默认参数位置:若需要默认参数,应放在
*args之后,并用 仅限关键字 或**kwargs接收。
# 基础用法:求任意个数之和def add(*nums): return sum(nums)
print(add(1, 2)) # 3print(add(1, 2, 3, 4)) # 10# 与位置参数混用def log(level, *messages): print(f"[{level}]", messages)
log("INFO", "start", "process", "done")# 输出: [INFO] start process done# 与关键字参数(**/ 或 *)组合def show(title, *items, sep=" "): print(title + sep.join(items))
show("Tags", "A", "B", sep=" | ")# 输出: Tags A | B5、可变关键字
5.1、简介
- 可变关键字参数允许函数接收 任意数量的“多余”关键字实参,并将它们打包成一个 字典(key 为字符串)。常用于 动态配置、装饰器、插件系统、日志封装 等需要“不定名参数”的场景。
5.2、语法规则
def func(pos1, pos2, *, kw_only="default", **kwargs): ...- 定义端:在形参名前加 两个星号 **,习惯命名为 **kwargs(kwargs 不是关键字,可改)。
- 位置约束:**kwargs 必须位于 所有其他形参之后。
- 调用端:多余的 关键字实参 被 收集成 dict。
5.3、常见错误
- **
**kwargs不在末尾**:语法错误。 - 键冲突:实参名与显式形参同名 → TypeError: 重复关键字参数。
# 基础用法:动态配置def connect(host, **options): cfg = {"host": host, options} return cfg
print(connect("api.io", ssl=True, timeout=30))# {'host': 'api.io', 'ssl': True, 'timeout': 30}# 与位置/关键字参数混用def log(msg, level="INFO", **tags): print(f"[{level}] {msg}", tags)
log("start", level="DEBUG", user="alice", code=200)# [DEBUG] start {'user': 'alice', 'code': 200}# 装饰器场景def timer(func): def wrapper(*args, **kwargs): start = time.perf_counter() result = func(*args, **kwargs) print("耗时", time.perf_counter() - start) return result return wrapper
@timerdef slow(n=1): time.sleep(n)
slow(n=2)四、函数返回值
1、语法规则
def 函数名(参数列表): 语句块 [return 表达式列表] # 可选- return 一旦执行,函数 立即终止,后续代码不再执行。
- 没有 return 或 return 后面无表达式 → 隐式返回 None。
2、返回值的 6 种形态
| 形态 | 示例 | 调用者接收 |
|---|---|---|
| 单一值 | return 42 | 42 |
| 多个值(打包元组) | return a, b | (a, b) |
| 列表/字典/对象 | return [1, 2] | [1, 2] |
| 生成器 | yield x | 生成器对象 |
| None(空函数) | 无 return | None |
| 提前 return | if cond: return | 提前终止 |
3、None 的两种语义
- 无意义函数:如纯副作用 print()。
- 占位/默认:如未找到结果返回 None,调用者可 if result is None: 判断。
4、多值返回(元组打包)
def divmod(a, b): return a // b, a % b
q, r = divmod(7, 3) # 元组自动解包print(q, r) # 2 15、提前 return 与守卫语句
def sqrt(x): if x < 0: return None # 提前退出 return x ** 0.56、类型注解(PEP 484)
from typing import List, Optional, Tuple
def parse(data: str) -> Optional[Tuple[int, str]]: if not data: return None return len(data), data.upper()7、底层实现(CPython 简述)
调用时栈帧压入实参 → 执行字节码 → 遇到 RETURN_VALUE 指令:
- 计算表达式结果 → 栈顶
- 弹出当前帧 → 返回值压入调用者栈顶
因此 返回对象引用,不额外拷贝大对象。