一、函数的基本写法
def function_name(parameter1, parameter2, ...):
"""
Docstring: 描述函数的功能、参数和返回值 (可选但强烈推荐)
"""
# 函数体: 实现功能的代码
# ...
return value # 可选,用于返回结果
def
关键字:用于声明开始定义一个函数。function_name
(函数名):遵循 Python 命名约定,通常由小写字母和下划线组成。parameter1, parameter2, …
(参数):也称为形参,是函数在被调用时接收输入值的变量。参数是可选的。()
(括号):参数列表需放在括号内,即使无参数,括号也不可省略。:
(冒号):标志函数定义头部结束,随后缩进的代码块为函数体。- Docstring(文档字符串):可选,位于函数定义第一行的多行字符串,用于解释函数作用、参数及返回值等信息。可通过
help(function_name)
或function_name.__doc__
查看。 - 函数体(Function Body):缩进的代码块,包含实现函数功能的语句。
return value
(返回语句):return
语句用于从函数返回一个值。若无return
语句或return
后无值,函数自动返回None
。函数可在不同条件分支中包含多个return
语句。
1、不带参数的函数
# 定义一个简单的问候函数
def greet():
"""打印一句问候语。"""
message = "大家好!欢迎学习Python函数定义!"
print(message)
greet()
# 查看文档字符串,方便查看函数的使用,这个方法可以不掌握
print(greet.__doc__)
# 实际上,当你在py文件中,鼠标悬停在函数上时按住ctrl即可点击函数跳转到其内部查看函数的定义
2、带参数的函数
形参(Parameters):函数定义中列出的变量名,如 name
、feature1
、feature2
等。形参相当于一个占位符,代表函数在被调用时将会接收到的数据。形参在函数内部有自己的作用域,它们就像函数内部定义的局部变量。
实参(Arguments):函数调用时传递给函数的实际值,如 "张三"
、10
、25
等。实参是实实在在的数据,它们会被赋值给函数定义中的形参。实参本身在函数外部有自己的作用域。
传递关系:当实参的值传递给形参后,在函数内部对形参的修改,一般不会影响到函数外部实参的值(对于不可变数据类型,如整数、字符串、元组等),如:
def change_number(num):
num = num + 1
return num
original_num = 5
new_num = change_number(original_num)
print(original_num)
print(new_num)
# 输出:
5
6
在这个例子中,形参
num
在函数change_number
内部被修改,但函数外部的original_num
值并没有改变。
然而,对于可变数据类型(如列表、字典),如果在函数内部通过形参修改了其内容,会影响到函数外部的实参。例如:
def change_list(lst):
lst.append(4)
return lst
my_list = [1, 2, 3]
new_list = change_list(my_list)
print(my_list)
print(new_list)
# 输出:
[1, 2, 3, 4]
[1, 2, 3, 4]
这里函数内部对形参
lst(
指向外部传入的my_list)
的修改,导致my_list
本身内容的改变。
3、带返回值的函数
# 定义一个计算和并返回结果的函数
def calculate_sum(a, b):
"""计算两个数的和并返回结果。
Args:
a (float or int): 第一个数。
b (float or int): 第二个数。
Returns:
float or int: 两个数的和。
"""
result = a + b
return result
print("hhh") # 此代码不会执行,因为函数遇到 return 语句后立即返回
calculate_sum(2, 3)
# 输出:5
二、变量作用域
局部变量(Local Variables):在函数内部定义的变量,仅在该函数内部有效,函数执行完毕后通常会被销毁。
全局变量(Global Variables):在所有函数外部定义的变量,可在程序任何地方访问(但在函数内部修改全局变量需使用 global
关键字声明,初学者可先避免)。
print("\n--- 变量作用域示例 ---")
global_var = "我是一个全局变量"
def scope_test():
# 声明使用的是全局变量
global global_var
local_var = "我是一个局部变量"
print(f"在函数内部,可以看到局部变量: '{local_var}'")
print(f"在函数内部,也可以看到全局变量: '{global_var}'")
global_var = "尝试在函数内修改全局变量"
print(f"在函数内部,修改后的 '全局' 变量: '{global_var}'")
scope_test()
print(f"\n在函数外部,可以看到全局变量: '{global_var}'")
# 会产生 NameError,因为 local_var 只在函数内存在
print(f"在函数外部,不能看到局部变量: {local_var}")
三、函数的参数类型
位置参数(Positional Arguments):
调用函数时按顺序匹配参数。
def describe_pet(animal_type, pet_name):
"""显示宠物的信息。"""
print(f"\n我有一只 {animal_type}.")
print(f"我的 {animal_type} 的名字叫 {pet_name}.")
describe_pet("猫", "咪咪")
# 输出:
我有一只 猫.
我的 猫 的名字叫 咪咪.
为提高可读性,推荐使用关键字参数传递所有参数。例如对于复杂函数:
# 位置参数写法不清晰
plot_data(data, x_col, y_col, "blue", "-", True, False, "My Plot", "X-axis", "Y-axis")
# 关键字参数写法更清晰
plot_data(data=my_data, x_column='time', y_column='value',
color='blue', linestyle='-', show_grid=True, use_log_scale=False,
title="My Awesome Plot", xlabel="Time (s)", ylabel="Value")
在 Python 函数调用中,关键字参数通过 “参数名 = 值” 的形式传递。这样一来,Python 解释器根据参数名来匹配值,而不是位置,即此时与赋值顺序无关。
默认参数值(Default Parameter Values):
定义函数时给参数指定默认值,调用时若未提供该参数,则使用默认值。注意,带默认值的参数必须放在无默认值的参数之后。
# animal_type 有默认值
def describe_pet_default(pet_name, animal_type="狗"):
"""显示宠物的信息,动物类型默认为狗。"""
print(f"我有一只 {animal_type}.")
print(f"我的 {animal_type} 的名字叫 {pet_name.title()}.")
# animal_type 使用默认值 "狗"
describe_pet_default(pet_name="小黑")
# 提供 animal_type时,覆盖默认值
describe_pet_default(pet_name="雪球", animal_type="仓鼠")
可变数量参数(*args 和 **kwargs):
*args
(收集位置参数):将多余的位置参数收集为一个元组。函数调用时,Python 先尝试用位置参数填充明确定义的、非关键字的形参(即普通无*或**前缀的参数
)。若填充后还有剩余位置参数,这些参数将被收集成元组赋值给 *args
指定的变量(通常为 args
)。若位置参数数量正好或少于明确定义的形参数量且满足必需参数,*args
为空元组 ()
。
def make_pizza(size, *toppings):
"""概述要制作的比萨。
*toppings 会将所有额外的位置参数收集到一个元组中。
"""
print(f"\n制作一个 {size} 寸的比萨,配料如下:")
if toppings: # 只要toppings不为空元组,就会执行
for topping in toppings:
print(f"- {topping}")
else:
print("- 原味 (无额外配料)")
make_pizza(12, "蘑菇")
make_pizza(16, "香肠", "青椒", "洋葱")
make_pizza(9) # toppings 会是空元组
**kwargs
(收集关键字参数):将多余的关键字参数收集为一个字典。函数调用时,Python 先处理位置参数,再尝试用关键字参数(形如 name=value)填充明确定义的同名形参。若填充后还有剩余关键字参数,这些参数将被收集成字典赋值给 **kwargs
指定的变量(通常为 kwargs
)。若所有关键字参数都能匹配明确形参名,**kwargs
为空字典 {}
。
def build_profile(first_name, last_name, **user_info):
"""创建一个字典,其中包含我们知道的有关用户的一切。
**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='物理学',
hobby='小提琴')
print(f"\n用户信息: {user_profile}")
# 输出: {'first_name': '爱因斯坦', 'last_name': '阿尔伯特', 'location': '普林斯顿', 'field': '物理学', 'hobby': '小提琴'}
当函数 build_profile
被调用时,Python 解释器首先将位置参数 '爱因斯坦'
和 '阿尔伯特'
分别赋值给 first_name
和 last_name
。然后,对于剩下的 location='普林斯顿', field='物理学', hobby='小提琴'
关键字参数,由于在函数定义中没有与之直接对应的普通形参,所以会被收集到 user_info
字典中。
*args 和 **kwargs 核心目的是让函数能接收不定数量的参数,并以元组和字典的形式在函数内部处理。即当位置参数用完就自动变成 *args,当关键词参数用完就自动变成**kwarges
# 同时出现 *args 和 **kwargs,注意参数的传入顺序
def process_data(id_num, name, *tags, status="pending", **details): # 注意,这里的status 是仅关键字参数,必须通过关键词传值
print(f"ID: {id_num}")
print(f"Name: {name}")
print(f"Tags (*args): {tags}")
print(f"Status: {status}") # status 是一个有默认值的普通关键字参数
print(f"Details (**kwargs): {details}")
print("-" * 20)
# 调用1:
process_data(101, "Alice", "vip", "new_user", location="USA", age=30)
# ID: 101
# Name: Alice
# Tags (*args): ('vip', 'new_user') <-- "vip", "new_user" 是多余的位置参数,被 *tags 收集
# Status: pending <-- status 使用默认值,因为调用中没有 status=...
# Details (**kwargs): {'location': 'USA', 'age': 30} <-- location 和 age 是多余的关键字参数,被 **details 收集
# --------------------
# 调用2:
process_data(102, "Bob", status="active", department="Sales")
# ID: 102
# Name: Bob
# Tags (*args): () <-- 没有多余的位置参数
# Status: active <-- status 被关键字参数 'active' 覆盖
# Details (**kwargs): {'department': 'Sales'} <-- department 是多余的关键字参数
# --------------------
# 调用3:
process_data(103, "Charlie", "admin") # 'admin' 会被 *tags 捕获
# ID: 103
# Name: Charlie
# Tags (*args): ('admin',)
# Status: pending
# Details (**kwargs): {}
# --------------------
# 调用4: (演示关键字参数也可以用于定义中的位置参数)
process_data(name="David", id_num=104, profession="Engineer")
# ID: 104
# Name: David
# Tags (*args): ()
# Status: pending
# Details (**kwargs): {'profession': 'Engineer'}
# --------------------