函数
约 1435 字大约 5 分钟
2026-05-27
函数把一段可复用逻辑封成名字。Python 里函数是一等对象:可以赋值、作参数、作返回值,这是装饰器与高阶函数的基础
定义与调用
定义并调用函数
def greet() -> None:
print("Hello, World!")
greet()def 执行时会创建一个函数对象,并绑定到名字 greet 上;调用时进入新的局部作用域(详见 作用域)
参数
位置参数
按定义顺序传入
位置参数
def add(a: int, b: int) -> int:
return a + b
print(add(3, 5)) # 8默认参数
调用时可省略,有默认值的参数必须写在无默认值参数后面
默认参数
def greet(name: str, greeting: str = "Hello") -> None:
print(f"{greeting}, {name}!")
greet("Alice")
greet("Bob", "Hi")
# def bad(greeting="Hello", name): # SyntaxError
# pass默认值在 定义时求值一次,可变对象作默认值会共享,见作用域篇 "默认参数的求值时机"
关键字参数
按名字传,顺序可打乱;与位置参数混用时,位置参数必须在前
关键字参数
def person_info(name: str, age: int, city: str) -> None:
print(f"{name}, {age}岁, 来自{city}")
person_info(age=25, city="北京", name="Alice")
person_info("Alice", age=25, city="北京")
# person_info(name="Alice", 25, "北京") # SyntaxError*args 与 **kwargs
*args 收集位置参数
def sum_all(*args: int) -> int:
total = 0
for num in args:
total += num
return total
print(sum_all(1, 2, 3)) # 6
print(sum_all()) # 0
def show_args(*args: int) -> None:
print(type(args), args) # <class 'tuple'> (1, 2, 3)
show_args(1, 2, 3)**kwargs 收集关键字参数
def print_info(**kwargs: object) -> None:
print(type(kwargs), kwargs)
for key, value in kwargs.items():
print(f"{key}: {value}")
print_info(name="Alice", age=25)*args 收集多余位置参数为元组,**kwargs 收集多余关键字参数为字典。名字 args / kwargs 只是惯例,关键是 * 与 **
参数定义顺序
参数顺序
def demo(a: int, b: int = 2, *args: int, **kwargs: int) -> None:
print(f"a={a}, b={b}")
print(f"args={args}")
print(f"kwargs={kwargs}")
demo(1, 3, 4, 5, x=10, y=20)
# a=1, b=3
# args=(4, 5)
# kwargs={'x': 10, 'y': 20}更完整的形态还包括 keyword-only 参数(* 后的具名参数)与 positional-only(/ 前的参数,Python 3.8+):
positional-only 与 keyword-only
def f(pos_only, /, normal, *, kw_only: int) -> None:
print(pos_only, normal, kw_only)
f(1, 2, kw_only=3)
f(1, normal=2, kw_only=3)
# f(pos_only=1, normal=2, kw_only=3) # TypeError解包传参
* 与 ** 解包传参
def add(a: int, b: int, c: int) -> int:
return a + b + c
values = (1, 2, 3)
print(add(*values))
mapping = {"a": 1, "b": 2, "c": 3}
print(add(**mapping))返回值
返回值与多返回值
def square(x: int) -> int:
return x ** 2
def min_max(numbers: list[int]) -> tuple[int, int]:
return min(numbers), max(numbers) # 实际返回一个元组
print(square(4))
lo, hi = min_max([3, 1, 4, 1, 5])
print(lo, hi) # 1 5
print(min_max([3, 1, 4])) # (1, 4)
def say_hello() -> None:
print("Hello")
result = say_hello()
print(result) # None,没有 return 就返回 Nonereturn 会立刻结束函数。多个 return 写在不同分支是常见写法;函数末尾隐式 return None
Docstring
文档字符串
def calculate_bmi(weight: float, height: float) -> float:
"""计算 BMI
参数:
weight: 体重,千克
height: 身高,米
返回:
BMI 数值
"""
return weight / (height ** 2)
print(calculate_bmi.__doc__)
print(calculate_bmi(70, 1.75))文档字符串是函数对象的 __doc__ 属性,工具与 IDE 会读它;风格可用 Google / NumPy / Sphinx 任选一种并保持一致
函数是对象
函数作为值传递
def inc(x: int) -> int:
return x + 1
f = inc
print(f(1))
def apply_twice(fn, x: int) -> int:
return fn(fn(x))
print(apply_twice(inc, 3)) # 5把函数当值传递,是 map / sorted(key=...) / 装饰器的共同前提
参数传递:对象引用
Python 没有 C 那种 "按值 / 按引用" 的简单二分。传参时绑定的是 对象的引用:
- 不可变对象(
int/str/tuple)在函数里 "改" 名字只会换绑定,调用方看不到 - 可变对象(
list/dict)若被原地修改,调用方会看到副作用
可变参数的副作用
def append_item(items: list[int], x: int) -> None:
items.append(x) # 改的是同一个 list 对象
data = [1, 2]
append_item(data, 3)
print(data) # [1, 2, 3]
def rebind(items: list[int]) -> None:
items = items + [9] # 局部名字指向新列表,不影响外部
print("内部", items)
rebind(data)
print("外部", data)需要隔离时,在函数入口拷贝:items = list(items),或约定调用方传入不应被改的数据
递归
函数可以直接或间接调用自己。必须有明确的终止条件,否则会 RecursionError
递归阶乘与斐波那契
def factorial(n: int) -> int:
if n < 0:
raise ValueError("n 必须非负")
if n <= 1:
return 1
return n * factorial(n - 1)
print(factorial(5))
def fib(n: int) -> int:
if n < 2:
return n
return fib(n - 1) + fib(n - 2)
print([fib(i) for i in range(8)])
# 默认递归深度有限,可用 sys.getrecursionlimit() 查看
import sys
print(sys.getrecursionlimit())能自然写成循环的场景,循环往往更省栈、更好调。递归更适合树、分治、回溯这类结构
类型标注是元数据
标注 不改变运行时行为,解释器不会因为写了 x: int 就拒绝传入字符串。它们主要服务类型检查器(mypy / pyright)和 IDE
标注存在 __annotations__
def add(a: int, b: int = 0) -> int:
return a + b
print(add.__annotations__)
print(add("x", "y")) # 运行时仍可能 "成功",类型检查才会报更完整的 Optional / Generic / Protocol 见后续 类型标注
函数属性与内省
函数对象上的常用属性
def greet(name: str = "world") -> str:
"""打个招呼"""
return f"Hello, {name}"
print(greet.__name__)
print(greet.__doc__)
print(greet.__defaults__) # 位置/默认参数的默认值元组
print(callable(greet), callable(42))装饰器会替换或包装函数对象,常用 functools.wraps 保留 __name__ / __doc__,详见后续装饰器篇
