主题
03 · 函数、模块与包管理
函数定义
js
function greet(name, prefix = '你好') {
return `${prefix},${name}`
}
greet('Lin', '欢迎')python
def greet(name, prefix="你好"):
"""生成一句问候语。""" # docstring,描述函数用途
return f"{prefix},{name}"
print(greet("Lin"))
print(greet("Lin", prefix="欢迎")) # 关键字参数位置参数按顺序匹配;关键字参数按名称匹配。client.create(model="...", messages=...) 用的正是关键字参数。函数没有显式 return 时返回 None。
类型注解先认识一下
python
def add(a: int, b: int) -> int:
return a + b: int 表示参数类型,-> int 表示返回类型。类似 TS 的 (a: number, b: number): number。它主要服务编辑器和静态检查器,并不是自动的运行时校验。
参数展开
python
def log(*args, **kwargs):
print(args) # tuple:所有额外的位置参数
print(kwargs) # dict:所有额外的关键字参数
log("hello", "agent", level="info")
params = {"a": 2, "b": 3}
print(add(**params)) # 等价于 add(a=2, b=3)* 展开序列,** 展开字典。Agent 将模型生成的参数 JSON 解析成 dict 后,经常用 function(**args) 调工具。
一个经典坑
不要用可变对象作为默认值:def append(item, items=[]):。默认 list 只创建一次,所有调用共享它。
正确写法:
python
def append(item, items=None):
if items is None:
items = []
items.append(item)
return items函数也是值
python
def double(x):
return x * 2
handlers = {"double": double}
print(handlers["double"](3)) # 6这就是后面「工具注册表」的基础:根据字符串工具名,从白名单里找到函数,而不是执行模型提供的任意代码。
模块:一个文件就是一个模块
创建两个文件:
python
def add(a: int, b: int) -> int:
return a + bpython
from math_tools import add
print(add(2, 3))同目录下运行 uv run main.py,得到 5。
另一种写法是 import math_tools,然后 math_tools.add(2, 3)。模块路径不带 .py。不要把自己的文件命名为 openai.py、json.py、typing.py,会遮蔽同名库。
程序入口
python
def main():
print("程序开始")
if __name__ == "__main__":
main()直接运行这个文件时执行 main;被别的文件 import 时不执行。避免 import 模块就触发 API 计费或开始交互。
标准库 vs 第三方库
json、os、pathlib、asyncio:Python 自带,不需要安装。openai、pydantic、httpx:第三方,先uv add。
bash
uv add httpx
uv run main.py
uv sync # 别人克隆项目后恢复依赖uv add 更新 pyproject.toml 和 uv.lock;这两个文件应提交,.venv 不提交。不要向系统 Python 全局安装项目依赖。
动手练习
定义 celsius_to_fahrenheit(celsius),放在 temperature.py,再从 main.py 导入并调用。公式:摄氏度 × 9 ÷ 5 + 32。
参考答案
python
def celsius_to_fahrenheit(celsius: float) -> float:
return celsius * 9 / 5 + 32python
from temperature import celsius_to_fahrenheit
print(celsius_to_fahrenheit(22)) # 71.6