跳到主要内容

函数

问题

Python 函数有哪些参数类型?什么是一等公民?闭包和 lambda 表达式怎么用?

答案

函数定义与参数类型

Python 函数支持非常灵活的参数机制:

def example(
pos_only, # 仅位置参数(3.8+, / 之前的参数)
/,
normal, # 普通参数(位置或关键字均可)
*,
kw_only, # 仅关键字参数(* 之后的参数)
default="value", # 默认参数
):
pass

# 可变参数
def func(*args, **kwargs):
print(args) # 元组:(1, 2, 3)
print(kwargs) # 字典:{"name": "Alice"}

func(1, 2, 3, name="Alice")

5 种参数类型

类型语法说明
位置参数def f(a, b)按位置传递
默认参数def f(a, b=1)有默认值,可选
可变位置def f(*args)收集多余位置参数为 tuple
可变关键字def f(**kwargs)收集多余关键字参数为 dict
仅位置/仅关键字def f(a, /, b, *, c)限制参数传递方式
可变默认参数陷阱

默认参数在函数定义时创建一次,可变默认值会在多次调用间共享:

# ❌ 错误
def append_to(item, lst=[]):
lst.append(item)
return lst

print(append_to(1)) # [1]
print(append_to(2)) # [1, 2] ← 不是 [2]!

# ✅ 正确:用 None 作为哨兵值
def append_to(item, lst=None):
if lst is None:
lst = []
lst.append(item)
return lst

函数是一等公民

Python 中函数是对象,可以赋值给变量、作为参数传递、作为返回值:

# 赋值给变量
greet = print
greet("Hello") # Hello

# 作为参数传递(高阶函数)
def apply(func, value):
return func(value)

result = apply(str.upper, "hello") # "HELLO"

# 存在数据结构中
operations = {
"+": lambda a, b: a + b,
"-": lambda a, b: a - b,
"*": lambda a, b: a * b,
}
print(operations["+"](1, 2)) # 3

lambda 表达式

# 匿名函数,只能包含一个表达式
square = lambda x: x ** 2
print(square(5)) # 25

# 常用于排序、过滤
students = [("Alice", 85), ("Bob", 92), ("Charlie", 78)]
students.sort(key=lambda s: s[1], reverse=True)
# [('Bob', 92), ('Alice', 85), ('Charlie', 78)]

# 配合 map/filter
nums = [1, 2, 3, 4, 5]
evens = list(filter(lambda x: x % 2 == 0, nums)) # [2, 4]
squared = list(map(lambda x: x ** 2, nums)) # [1, 4, 9, 16, 25]

闭包

闭包是引用了外层函数变量的内层函数,即使外层函数已返回,变量仍然存活:

def make_multiplier(factor):
# factor 被内层函数引用,形成闭包
def multiplier(x):
return x * factor
return multiplier

double = make_multiplier(2)
triple = make_multiplier(3)
print(double(5)) # 10
print(triple(5)) # 15

# 查看闭包变量
print(double.__closure__[0].cell_contents) # 2
闭包中循环变量的陷阱
# ❌ 错误:所有函数共享同一个 i
funcs = []
for i in range(3):
funcs.append(lambda: i)

print([f() for f in funcs]) # [2, 2, 2]

# ✅ 正确:用默认参数捕获当前值
funcs = []
for i in range(3):
funcs.append(lambda i=i: i)

print([f() for f in funcs]) # [0, 1, 2]

函数注解(Type Hints)

def greet(name: str, times: int = 1) -> str:
"""向某人问候指定次数"""
return (f"Hello, {name}! " * times).strip()

# 注解不影响运行,但有助于 IDE 提示和静态检查
print(greet.__annotations__)
# {'name': <class 'str'>, 'times': <class 'int'>, 'return': <class 'str'>}

常见面试问题

Q1: *args**kwargs 是什么?有什么用?

答案

  • *args:将多余的位置参数收集为一个 tuple
  • **kwargs:将多余的关键字参数收集为一个 dict

主要用途:编写接受任意参数的函数,常见于装饰器和函数转发:

def wrapper(*args, **kwargs):
"""透传所有参数"""
return original_func(*args, **kwargs)

Q2: Python 中参数传递是值传递还是引用传递?

答案

都不是。Python 使用赋值传递(pass by assignment),也叫"传对象引用":

  • 函数参数接收的是对象的引用(指针)
  • 对不可变对象:修改会创建新对象,不影响外部
  • 对可变对象:原地修改会影响外部
def modify(x, lst):
x = 100 # 重新绑定,不影响外部
lst.append(4) # 原地修改,影响外部

a = 1
b = [1, 2, 3]
modify(a, b)
print(a) # 1 ← 不变
print(b) # [1, 2, 3, 4] ← 被修改

Q3: 什么是装饰器?它和闭包的关系?

答案

装饰器是闭包的一种应用模式。装饰器本质是一个接收函数作为参数、返回新函数的高阶函数:

# 装饰器 = 闭包 + 高阶函数
def decorator(func): # 接收函数
def wrapper(*args, **kwargs): # 闭包:引用了外层的 func
print("Before")
result = func(*args, **kwargs)
print("After")
return result
return wrapper # 返回新函数

详见 装饰器 专题。

Q4: 如何实现函数缓存/记忆化?

答案

from functools import lru_cache

@lru_cache(maxsize=128)
def fibonacci(n: int) -> int:
if n < 2:
return n
return fibonacci(n - 1) + fibonacci(n - 2)

print(fibonacci(100)) # 瞬间返回
print(fibonacci.cache_info()) # CacheInfo(hits=98, misses=101, ...)

Python 3.9+ 还有 @functools.cache(无限缓存,等价于 @lru_cache(maxsize=None))。

Q5: nonlocalglobal 关键字的作用?

答案

  • global:在函数内声明变量为模块全局变量
  • nonlocal:在嵌套函数内声明变量为外层函数的局部变量
count = 0

def outer():
x = 10

def inner():
nonlocal x # 修改外层函数的 x
global count # 修改全局的 count
x += 1
count += 1

inner()
print(x) # 11(被 nonlocal 修改)

outer()
print(count) # 1(被 global 修改)

相关链接