创建时间: 2026-08-25最后更新: 2026-08-27

1. 定义

函数是一段可以重复调用的代码。把一组有明确职责的操作放进函数后,其他地方只需要调用函数名,不必重复编写内部细节。

使用 def 定义函数,函数体通过缩进表示:

hello.py
1
def say_hello():
2
print("你好,Python")
3
4
say_hello()

def say_hello(): 只是创建函数,下面的 say_hello() 才是真正调用它。调用时必须保留括号,即使函数没有参数也一样。

函数可以接收参数,让同一段逻辑处理不同的数据:

greet.py
1
def greet(name):
2
print(f"你好,{name}")
3
4
greet("小明")
5
greet("小红")

函数名应该表达动作或结果,例如 calculate_total()、load_user()、format_message()。一个函数最好只负责一类事情,函数太长或同时做很多不相关的事情时,可以继续拆分。

可以使用文档字符串说明函数用途,方便自己和其他人阅读:

docstring.py
1
def add(left, right):
2
"""返回两个数字的和。"""
3
return left + right
4
5
print(add.__doc__)

2. 参数

参数写在函数定义的括号中,调用时传入的值叫作实参:

parameters.py
1
def introduce(name, age):
2
print(f"{name} 今年 {age} 岁")
3
4
introduce("小明", 18)

上面的 name 和 age 是参数,"小明" 和 18 是实参。默认情况下,Python 按照位置把实参依次传给参数。

可以为参数设置默认值。调用时不传这个参数,就使用默认值:

default.py
1
def greet(name, greeting="你好"):
2
print(f"{greeting},{name}")
3
4
greet("小明")
5
greet("小红", "早上好")

有默认值的参数必须放在没有默认值的参数后面:

parameter-order.py
1
def connect(host, port=8000):
2
print(f"连接 {host}:{port}")
3
4
connect("localhost")
5
connect("localhost", 9000)

不要把列表、字典等可变对象直接作为默认值。默认值只会在函数定义时创建一次,多次调用可能会共享同一个对象:

mutable-default.py
1
def add_topic(topic, topics=None):
2
if topics is None:
3
topics = []
4
5
topics.append(topic)
6
return topics
7
8
print(add_topic("变量"))
9
print(add_topic("列表"))

使用 None 作为默认值,再在函数内部创建新列表,是更安全的写法。

3. 返回

函数可以使用 return 把结果交给调用方:

return.py
1
def add(left, right):
2
return left + right
3
4
result = add(2, 3)
5
print(result) # 5

return 会立即结束当前函数。它后面的代码不会继续执行:

early-return.py
1
def check_age(age):
2
if age < 18:
3
return "未成年"
4
5
return "已成年"
6
7
print(check_age(16))

如果函数没有写 return,或者只写了没有返回值的 return,函数结果就是 None:

none-return.py
1
def print_message(message):
2
print(message)
3
4
result = print_message("处理完成")
5
print(result) # None

函数可以返回多个值。Python 会把它们组合成一个元组,调用方可以直接解包:

multiple-return.py
1
def calculate(numbers):
2
total = sum(numbers)
3
count = len(numbers)
4
return total, total / count
5
6
total, average = calculate([80, 90, 70])
7
print(total) # 240
8
print(average) # 80.0

函数应该返回结果,而不是只依赖 print() 输出。print() 适合展示信息,return 才能让调用方继续使用这个值:

print-vs-return.py
1
def wrong_add(left, right):
2
print(left + right)
3
4
def correct_add(left, right):
5
return left + right
6
7
wrong_result = wrong_add(1, 2) # 打印 3,但结果是 None
8
correct_result = correct_add(1, 2) # 结果是 3
9
print(correct_result)

4. 调用

调用函数时,可以按位置传参,也可以按参数名传参。关键字参数更容易表达每个值的含义:

keyword.py
1
def create_user(name, age, city):
2
return {"name": name, "age": age, "city": city}
3
4
user = create_user(age=18, city="上海", name="小明")
5
print(user)

位置参数必须放在关键字参数前面,关键字参数之间的顺序可以调整:

mixed-arguments.py
1
def connect(host, port, timeout=30):
2
print(host, port, timeout)
3
4
connect("localhost", port=8000, timeout=10)

在参数前加 *,可以把多个位置参数收集成一个元组:

args.py
1
def total(*numbers):
2
return sum(numbers)
3
4
print(total(1, 2, 3))
5
print(total(10, 20, 30, 40))

在参数前加 **,可以把多个关键字参数收集成一个字典:

kwargs.py
1
def show_options(**options):
2
for key, value in options.items():
3
print(f"{key}:{value}")
4
5
show_options(model="deepseek-chat", temperature=0.2)

如果希望某些参数只能使用关键字传入,可以在它们前面使用单独的 *:

keyword-only.py
1
def request(path, *, timeout=30):
2
print(path, timeout)
3
4
request("/users", timeout=10)
5
# request("/users", 10) # TypeError

*args、**kwargs 很灵活,但会降低函数签名的清晰度。只有确实需要接收不定数量参数时再使用它们,普通函数优先写出明确的参数名。

5. 作用域

函数内部创建的变量通常是局部变量,只能在函数内部使用:

local-scope.py
1
def calculate():
2
result = 2 + 3
3
return result
4
5
print(calculate())
6
# print(result) # NameError

函数外部的变量属于外部作用域。函数可以读取外部变量,但如果要在函数内部修改外部变量,最好通过参数和返回值传递,而不是依赖全局变量:

scope.py
1
default_timeout = 30
2
3
def get_timeout():
4
return default_timeout
5
6
print(get_timeout())

参数和局部变量可以遮蔽同名的外部变量:

shadow.py
1
name = "外部名字"
2
3
def show_name(name):
4
print(name)
5
6
show_name("函数参数")
7
print(name) # 外部名字

类型提示可以写在参数和返回值旁边,帮助编辑器提示和读者理解数据类型。它不会自动阻止错误类型传入,运行时校验仍然需要 Pydantic 等工具:

type-hints.py
1
def add(left: int, right: int) -> int:
2
return left + right
3
4
print(add(2, 3))

6. 组合

函数可以组合使用,一个函数调用另一个函数。把清理、校验和组装分别放进小函数,代码更容易测试和复用:

compose.py
01
def clean_question(question: str) -> str:
02
return question.strip()
03
04
def is_valid_question(question: str) -> bool:
05
return len(question) > 0
06
07
def prepare_question(question: str) -> str | None:
08
cleaned = clean_question(question)
09
10
if not is_valid_question(cleaned):
11
return None
12
13
return cleaned
14
15
print(prepare_question(" 介绍一下 Python "))
16
print(prepare_question(" "))

在 FastAPI 和 LangChain 项目中,可以把请求数据组装为独立函数:

chat-request.py
01
def build_chat_request(question: str, model: str = "deepseek-chat") -> dict:
02
return {
03
"model": model,
04
"messages": [
05
{"role": "user", "content": question},
06
],
07
}
08
09
request = build_chat_request("请介绍一下 Python。")
10
print(request)

这样,接口入口只需要接收请求并调用 build_chat_request(),模型名称和消息结构的组装逻辑集中在一个地方。以后更换模型字段或增加参数时,只需要修改这个函数。

函数也可以接收另一个函数作为参数。Python 中函数也是对象,因此可以把行为传给通用函数:

callback.py
1
def apply_twice(value, action):
2
return action(action(value))
3
4
def add_exclamation(text):
5
return text + "!"
6
7
print(apply_twice("你好", add_exclamation)) # 你好!!

初学时不必急着大量使用高阶函数,但理解「函数可以被传递」后,会更容易读懂排序、异步回调和 LangChain 链式组合中的代码。

正在验证登录状态
请稍候,验证完成后将继续显示文章内容