Python 函数与参数传递
函数是把一段逻辑命名并复用的最小单位。这一篇讲清参数怎么传、默认值为什么危险、以及变量作用域。
基本定义
def greet(name, greeting="你好"):
return f"{greeting},{name}"
print(greet("小明")) # 你好,小明
print(greet("小明", "早上好")) # 早上好,小明
没有 return 或 return 后面没有值,函数返回 None。
位置参数与关键字参数
def connect(host, port, timeout=5):
print(host, port, timeout)
connect("127.0.0.1", 8080) # 位置参数
connect("127.0.0.1", port=8080, timeout=1) # 关键字参数
规则:位置参数必须写在关键字参数之前。混合使用时,可读性优先用关键字参数,尤其是布尔开关:
# 不好:看不出 True 是什么
send(data, True)
# 好
send(data, compress=True)
默认值的可变对象陷阱
这是 Python 最经典的坑:
def add_item(item, target=[]): # 危险
target.append(item)
return target
print(add_item("a")) # ['a']
print(add_item("b")) # ['a', 'b'] 上一轮的列表还在
默认值在函数定义时求值一次,之后所有调用共用同一个列表对象。正确写法:
def add_item(item, target=None):
if target is None:
target = []
target.append(item)
return target
任意数量参数
def total(*numbers):
return sum(numbers)
print(total(1, 2, 3)) # 6
def build_url(**parts):
print(parts)
build_url(scheme="https", host="example.com")
# {'scheme': 'https', 'host': 'example.com'}
*args 收集多余的位置参数为元组,**kwargs 收集多余的关键字参数为字典。参数顺序固定为:位置参数 → 默认参数 → *args → 关键字专用参数 → **kwargs。
强制调用方必须写参数名,用 * 分隔:
def create_user(*, name, admin=False):
print(name, admin)
create_user(name="ann") # 正确
create_user("ann") # TypeError
作用域
函数内部读取外部变量没问题,但赋值会创建局部变量:
count = 0
def bump():
count += 1 # UnboundLocalError
return count
Python 在编译函数时看到 count = ...,就把 count 判定为局部变量,执行到 count += 1 时发现还没定义。解决办法:
def bump():
global count
count += 1
return count
不过更好的做法是避免用全局变量,把状态显式地传进去、返回出来。
类型注解
注解不参与运行时检查,但能让编辑器和读者少犯错:
def average(values: list[float]) -> float:
return sum(values) / len(values)
配合 mypy 或 pyright 可以在提交前发现类型错误。
小结
- 位置参数在前,关键字参数在后;布尔开关用关键字写法。
- 默认值绝不用可变对象,用
None加判断。 - 函数内赋值会创建局部变量,改全局要
global,但最好别这么设计。 - 注解不是运行时校验,是给人和工具的说明。