1. 什么是函数?
想象一下,你正在写一个程序,需要反复计算两个数的和并打印出来。你可能会这样写:
a = 5
b = 10
sum_val = a + b
print(f"{a} 和 {b} 的和是 {sum_val}")
c = 20
d = 30
sum_val = c + d
print(f"{c} 和 {d} 的和是 {sum_val}")
# ... 如果还需要计算,就得再写一遍 ...
你会发现,这段代码很重复。如果计算逻辑稍微复杂一点,比如要计算平均值、方差等,重复的代码会更多,而且一旦逻辑需要修改,你就得去改每一个地方,非常麻烦。
函数(Function) 就是为了解决这个问题而生的。它是一段可重用的代码块,你可以给它起一个名字,然后在需要的时候随时调用(Call)它。
我们可以把上面的代码改写成一个函数:
# 定义一个名为 'add_and_print' 的函数
def add_and_print(num1, num2):
sum_val = num1 + num2
print(f"{num1} 和 {num2} 的和是 {sum_val}")
# 现在,需要计算时,只需调用函数即可
add_and_print(5, 10)
add_and_print(20, 30)
add_and_print(100, -50)
输出结果:
5 和 10 的和是 15
20 和 30 的和是 50
100 和 -50 的和是 50
看,代码是不是变得简洁、清晰多了?这就是函数的核心价值:代码复用、提高模块化和可读性。
2. 如何定义一个函数
Python中定义函数的语法非常简单,使用 def 关键字。
def function_name(parameter1, parameter2, ...):
"""
这里是文档字符串(Docstring),用来解释函数的功能。
这是一个好习惯!
"""
# 函数体(Function Body)
# 这里是函数的具体逻辑代码
return some_value # 使用 return 关键字返回结果
让我们分解一下这个结构:
| 组成部分 | 说明 |
|---|---|
def | 定义函数的关键字,告诉Python:“嘿,我要创建一个新函数了!” |
function_name | 你给函数起的名字。命名规则和变量一样(通常用小写字母和下划线,例如 calculate_average) |
() | 括号是必须的 |
parameter1, parameter2 | 参数(Parameters),它们是函数接收的输入值。就像是函数内部的临时变量。参数是可选的,函数也可以没有参数 |
: | 冒号也是必须的,表示函数定义的开始 |
| 文档字符串(Docstring) | 一个可选的字符串,用于解释函数的作用、参数和返回值。强烈建议为你的函数编写文档字符串,这样你或其他人将来能快速理解它的用途 |
| 函数体 | 缩进的代码块,包含了函数要执行的所有操作 |
return | return 关键字用于从函数中返回一个结果。执行到 return 时,函数会立即结束。如果一个函数没有 return 语句,它会默认返回 None |
3. 参数(Arguments)的多种形式
参数是函数最灵活的部分,Python提供了多种定义和传递参数的方式。
a. 位置参数 (Positional Arguments)
这是最常见的类型。调用函数时,传递的值会按照位置顺序依次赋值给对应的参数。
def describe_pet(animal_type, pet_name):
print(f"我有一只{animal_type}。")
print(f"它的名字叫{pet_name}。")
describe_pet("仓鼠", "哈哈") # "仓鼠" 对应 animal_type, "哈哈" 对应 pet_name
⚠️ 如果你搞错了顺序,结果也会出错:
describe_pet("哈哈", "仓鼠") # 输出就会很奇怪
b. 关键字参数 (Keyword Arguments)
为了避免位置混乱,你可以使用 参数名=值 的形式来传递参数。这样就无需关心参数的顺序了。
describe_pet(animal_type="猫", pet_name="咪咪")
describe_pet(pet_name="旺财", animal_type="狗") # 顺序变了,但结果正确
c. 默认参数 (Default Arguments)
在定义函数时,可以为参数指定一个默认值。如果在调用函数时没有提供该参数的值,它就会使用这个默认值。
⚠️ 注意:带默认值的参数必须放在没有默认值的参数后面!!!
def describe_pet(animal_type, pet_name="大黄"): # pet_name 有了默认值
print(f"我有一只{animal_type}。")
print(f"它的名字叫{pet_name}。")
describe_pet("狗") # 没有提供 pet_name,使用默认值 "大黄"
describe_pet("兔子", "小白") # 提供了,就使用 "小白"
d. 限定参数类型
在定义函数时,可以为参数指定一个参数类型。加入参数类型的好处有:
- 更易读:一看就知道函数期望什么类型的输入
- 更易维护:在大型项目中,类型提示(Type Hints)是极佳的文档
- 能被IDE(如PyCharm, VSCode)更好地支持:获得智能提示和错误检查
这种限制参数类型的功能,在Python中被称为 类型提示(Type Hints) 或 类型注解(Type Annotations)。
⚠️ 注意:类型提示只是一个”提示”或”注解”,Python解释器在运行时不会强制检查类型。如果一个字符串被传递给了期望是整数的参数,代码仍然会运行(并可能在函数内部某处报错)。它的主要目的是给开发者和工具(如IDE、静态类型检查器mypy)看的。
def describe_pet(animal_type: int, pet_name="大黄"): # animal_type 类型为 int
print(f"我有一只{animal_type}。")
print(f"它的名字叫{pet_name}。")
describe_pet("狗") # 给 animal_type 传 str,依旧正常运行
e. 可变参数 (*args 和 **kwargs)
有时候,你希望函数能处理任意数量的参数。
*args (非关键字可变参数)
它会把多余的位置参数收集成一个元组(Tuple)。
def make_pizza(size, *toppings):
print(f"制作一个 {size} 寸的披萨,配料如下:")
for topping in toppings:
print(f"- {topping}")
make_pizza(12, "蘑菇")
make_pizza(9, "香肠", "青椒", "奶酪")
**kwargs (关键字可变参数)
它会把多余的关键字参数收集成一个字典(Dictionary)。
def build_profile(first_name, last_name, **user_info):
profile = {}
profile['first_name'] = first_name
profile['last_name'] = last_name
for key, value in user_info.items():
profile[key] = value
return profile
user_profile = build_profile("爱因斯坦", "阿尔伯特",
location="普林斯顿",
field="物理")
print(user_profile)
# 输出: {'first_name': '爱因斯坦', 'last_name': '阿尔伯特', 'location': '普林斯顿', 'field': '物理'}
扩展
Python函数参数的完整顺序规则如下,这是一个不可违反的硬性规定,必须按照顺序定义:
【位置或关键字参数】(无默认值) -> 【位置或关键字参数】(有默认值) -> 【*args】 -> 【仅关键字参数】(可选) -> 【**kwargs】
def complex_function(
# 1. 位置参数(无默认值,必须传)- 最前面
mandatory_arg1: int,
mandatory_arg2: str,
# 2. 位置或关键字参数(有默认值)- 中间
default_arg1: int = 100,
default_arg2: str = "hello",
# 3. 可变位置参数 *args
*args: str,
# 4. 仅关键字参数(无默认值)
keyword_only1: int,
keyword_only2: str,
# 4.1 仅关键字参数(有默认值)
keyword_only_with_default: int = 200,
# 5. 可变关键字参数 **kwargs
**kwargs: str
) -> None:
"""
一个包含所有类型参数的复杂函数示例
"""
print(f"必须传的位置参数: {mandatory_arg1}, {mandatory_arg2}")
print(f"有默认值的位置或关键字参数: {default_arg1}, {default_arg2}")
if args:
print(f"可变位置参数 *args: {args}")
print(f"仅关键字参数: {keyword_only1}, {keyword_only2}, {keyword_only_with_default}")
if kwargs:
print(f"可变关键字参数 **kwargs: {kwargs}")
# 正确的调用方式
complex_function(
10, # mandatory_arg1
"mandatory", # mandatory_arg2
# default_arg1 和 default_arg2 有默认值,可以不传
"arg1", "arg2", # 这些会被 *args 接收
keyword_only1=30, # 必须用关键字传
keyword_only2="world", # 必须用关键字传
# keyword_only_with_default 有默认值,可以省略
extra1="value1", # 会被 **kwargs 接收
extra2="value2" # 会被 **kwargs 接收
)
另外参数中还可能有 / 和 *,简要了解即可:
def advanced_example(
positional_only, # 必须用位置传参
/, # / 之前的参数只能用位置传参
positional_or_keyword,
*,
keyword_only # * 之后的参数只能用关键字传参
):
pass
# 以下两种调用方式都可以
advanced_example("positional_only", "keyword", keyword_only="keyword_only")
advanced_example("positional_only", positional_or_keyword="keyword", keyword_only="keyword_only")
4. 返回值 (return)
函数不仅可以做事(比如打印),更重要的是它可以”产出”结果。return 语句就是函数的出口。
a. 返回单个值
def add(a, b):
return a + b
result = add(10, 20) # result 的值现在是 30
print(result)
b. 返回多个值
Python函数可以一次返回多个值,它们会被打包成一个元组。
def get_name_and_age():
name = "Tom"
age = 25
return name, age # 实际上是返回了 ('Tom', 25)
user_name, user_age = get_name_and_age() # 可以用多个变量来接收(解包)
print(f"姓名: {user_name}, 年龄: {user_age}")
c. 返回空值
如果函数执行完毕都没有遇到 return,或者只有一个空的 return,它会默认返回 None。None 是Python中一个特殊的值,表示”什么都没有”。
def do_nothing():
print("我做了一些事,但没有返回任何东西")
result = do_nothing()
print(result) # 输出: None
5. 函数也是一等公民
Python中的一个核心特性:函数是”一等公民”(First-Class Citizens)。
这意味着,函数可以像任何其他数据类型(比如整数、字符串、列表)一样被对待:
- 可以被赋值给一个变量
- 可以作为参数传递给另一个函数
- 可以作为另一个函数的返回值
- 可以存储在数据结构中
简单来说,在Python中,函数和整数、字符串、列表等数据类型拥有完全相同的”权利”。让我们通过具体例子来理解这4个特性:
特性1:函数可以被赋值给变量
def say_hello(name):
return f"Hello, {name}!"
# 1. 把函数赋值给一个变量
greet = say_hello # 注意:没有括号,我们是在引用函数本身
print(say_hello("Alice")) # 输出: Hello, Alice!
print(greet("Bob")) # 输出: Hello, Bob!
print(type(say_hello)) # 输出: <class 'function'>
print(type(greet)) # 输出: <class 'function'>
say_hello这个函数被赋值给了greet变量,就像你把数字5赋值给变量一样自然。
特性2:函数可以作为参数传递给其他函数
这是理解高阶函数和回调函数的基础。
def add(a, b):
return a + b
def multiply(a, b):
return a * b
def apply_operation(num1, num2, operation_func):
"""
operation_func 是一个函数参数
"""
result = operation_func(num1, num2)
print(f"运算结果是: {result}")
# 把函数作为参数传递
apply_operation(10, 20, add) # 输出: 运算结果是: 30
apply_operation(10, 20, multiply) # 输出: 运算结果是: 200
实际应用:自定义排序
students = ["Alice", "Bob", "Charlie", "David"]
# sorted() 函数的 key 参数接收的就是一个函数
sorted_by_length = sorted(students, key=len)
print(sorted_by_length) # ['Bob', 'Alice', 'David', 'Charlie']
特性3:函数可以作为其他函数的返回值
这个特性是闭包(Closure)和装饰器的基础。
def get_greeter(greeting_style):
"""
这个函数返回另一个函数
"""
if greeting_style == "formal":
def formal_greet(name):
return f"Good day, {name}."
return formal_greet
else:
def casual_greet(name):
return f"Hey {name}!"
return casual_greet
# 获得不同的问候函数
formal_greeter = get_greeter("formal")
casual_greeter = get_greeter("casual")
print(formal_greeter("Mr. Smith")) # 输出: Good day, Mr. Smith.
print(casual_greeter("Tom")) # 输出: Hey Tom!
特性4:函数可以存储在数据结构中
def add(a, b):
return a + b
def subtract(a, b):
return a - b
def multiply(a, b):
return a * b
# 把函数放在列表中
operations = [add, subtract, multiply]
a, b = 15, 5
for operation in operations:
result = operation(a, b)
print(f"{operation.__name__}: {result}")
# 输出
# add: 20
# subtract: 10
# multiply: 75
💡 在使用中需要注意区分带不带括号:
func是函数本身,func()是调用函数。
📌 “函数是一等公民”意味着在Python中,函数享有和所有其他数据类型完全相同的权利。这个看似简单的特性,实际上是Python函数式编程能力、装饰器、回调机制等高级特性的基石。
理解这个概念,就能明白为什么Python能做这么多灵活而强大的事情!
6. 变量的作用域 (Scope)
这是一个非常重要的概念!变量不是在程序的任何地方都可以访问的,它有自己的”作用范围”。
局部变量 (Local Scope)
在函数内部定义的变量,只在该函数内部有效。函数执行结束后,它就会被销毁。
def my_func():
message = "Hello, I am a local variable."
print(message)
my_func()
# print(message) # 这行会报错!因为在函数外部无法访问 message
全局变量 (Global Scope)
在所有函数之外定义的变量,可以在程序的任何地方被访问。
app_name = "我的应用"
def print_app_name():
print(f"欢迎来到 {app_name}") # 可以读取全局变量
print_app_name()
⚠️ 注意:通常不建议在函数内部直接修改全局变量,这会让代码难以维护。如果确实需要修改,要使用
global关键字声明。