Python 函数与参数传递

Python 3.13

函数是把一段逻辑命名并复用的最小单位。这一篇讲清参数怎么传、默认值为什么危险、以及变量作用域。

基本定义

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,但最好别这么设计。
  • 注解不是运行时校验,是给人和工具的说明。

下一步