Skip to content

函数、参数与作用域 ​

学习目标 ​

  • 定义有清晰参数和返回值的函数。
  • 区分位置参数、关键字参数、默认参数、可变参数和仅限关键字参数。
  • 理解 LEGB 作用域规则,避免可变默认值和隐式全局状态。

核心知识点 ​

函数是一等对象,可以赋值、传参和返回。调用时参数按位置或名称绑定;局部变量遵循 LEGB(Local、Enclosing、Global、Builtins)查找。函数应尽量接收输入、返回结果,把副作用放在边界。

常用用法 ​

def 定义函数与 return ​

python
def add(left: int, right: int) -> int:
    """返回两个整数之和。"""
    return left + right


print(add(2, 3))
# 输出:5

没有显式 return 时函数返回 None;返回多个值本质上是返回一个元组。

默认参数 ​

python
def greet(name: str, punctuation: str = "!") -> str:
    return f"Hello, {name}{punctuation}"


print(greet("Ada"), greet("Lin", "."))
# 输出:Hello, Ada! Hello, Lin.

默认值在函数定义时计算,适合不可变常量;可变对象不要直接做默认值。

关键字参数与位置参数 ​

python
def connect(host: str, port: int, *, timeout: float = 3.0) -> str:
    return f"{host}:{port} timeout={timeout}"


print(connect("localhost", 8000, timeout=1.5))
# 输出:localhost:8000 timeout=1.5

* 后的参数只能用关键字传递,能让调用更易读并保留扩展空间。

*args 与 **kwargs ​

python
def summarize(first: int, *numbers: int, unit: str = "个", **metadata: str) -> str:
    total = first + sum(numbers)
    return f"{total}{unit},标签={metadata.get('tag', 'none')}"


print(summarize(1, 2, 3, unit="项", tag="demo"))
# 输出:6项,标签=demo

*args 收集额外位置参数为元组,**kwargs 收集额外关键字参数为字典。

参数解包调用 ​

python
def rectangle(width: int, height: int) -> int:
    return width * height


values = (4, 5)
options = {"width": 6, "height": 7}
print(rectangle(*values), rectangle(**options))
# 输出:20 42

解包对象的结构必须匹配函数签名,关键字字典的键必须是参数名。

作用域与局部变量 ​

python
count = 10


def read_count() -> int:
    count = 20
    return count


print(read_count(), count)
# 输出:20 10

函数内赋值默认创建局部名字,不会改写同名全局变量。

lambda 与高阶函数 ​

python
records = [("Ada", 3), ("Lin", 1)]
ordered = sorted(records, key=lambda record: record[1])
print(ordered)
# 输出:[('Lin', 1), ('Ada', 3)]

lambda 只适合单表达式小函数;需要文档、分支或测试时写 def。

不常用但需要知道 ​

nonlocal ​

python
def make_counter():
    value = 0

    def next_value():
        nonlocal value
        value += 1
        return value

    return next_value


counter = make_counter()
print(counter(), counter())
# 输出:1 2

nonlocal 修改最近的外层函数作用域,常用于闭包状态;共享状态复杂时考虑类。

global ​

python
status = "idle"


def set_status() -> None:
    global status
    status = "ready"


set_status()
print(status)
# 输出:ready

全局可写状态会增加测试和并发难度,通常用返回值或对象封装替代。

functools.partial ​

python
from functools import partial


def multiply(left: int, right: int) -> int:
    return left * right


double = partial(multiply, 2)
print(double(4))
# 输出:8

partial 固定一部分参数,适合把已有函数适配到回调接口。

易混点 ​

  • 参数默认值只计算一次,def f(items=[]): ... 会跨调用共享列表。
  • return a, b 等于返回 (a, b),调用处可解包。
  • *args 是元组,**kwargs 是字典,不是任意“多个参数”。
  • 函数内赋值会使名字成为局部变量,先读取再赋值可能触发 UnboundLocalError。
  • 类型提示不负责运行时校验,边界数据仍要显式验证。

课后小问 ​

  1. 为什么不推荐用列表作为默认参数? 答案:默认列表在定义函数时只创建一次,多个调用会共享它。 解析:使用 None 作为哨兵,在函数体内创建新列表即可避免状态泄漏。

  2. *args 和 **kwargs 分别收集什么? 答案:前者收集额外位置参数,后者收集额外关键字参数。 解析:它们也可以在调用时用于解包,但方向相反:收集发生在定义处,解包发生在调用处。

本节小结 ​

先用明确签名写函数,再按需加入关键字专属参数、解包和闭包;让函数少依赖全局状态,代码更容易复用和测试。

快速回顾 ​

  • 定义:def、return
  • 参数:默认、关键字、*args、**kwargs
  • 调用解包:*values、**options
  • 作用域:LEGB、nonlocal、谨慎 global
  • 高阶函数:lambda、partial