目录
- 基本概念:参数 vs. 参数
- 四种参数类型
- 位置参数
- 关键字参数
- 默认参数
- 可变长度参数 (
*args和**kwargs)
- 参数传递:传值还是传引用?
- 高级用法
- 仅位置参数
- 仅关键字参数
- 参数解包
- 最佳实践
基本概念:参数 vs. 参数
在学习之前,我们先明确两个容易混淆的术语:

-
Parameter (形参): 指在定义函数时,函数名后面括号里的变量,它们是函数接收数据的占位符。
def my_function(name, age): # name 和 age 就是形参 print(f"Name: {name}, Age: {age}") -
Argument (实参): 指在调用函数时,传递给函数的实际值。
my_function("Alice", 30) # "Alice" 和 30 就是实参
Parameter 是函数的“菜单”,而 Argument 是你点的“菜”。
四种参数类型
Python 函数的参数定义非常灵活,主要分为以下四种。

a) 位置参数
这是最基本、最常见的参数类型,实参的顺序必须和形参的顺序一一对应。
def describe_pet(animal_type, pet_name):
"""显示宠物信息"""
print(f"\nI have a {animal_type}.")
print(f"Its name is {pet_name.title()}.")
# 正确的调用
describe_pet("dog", "willie")
# 输出:
# I have a dog.
# Its name is Willie.
# 错误的调用(顺序错误)
# describe_pet("willie", "dog") # 这会导致逻辑错误,程序会说 "I have a willie. Its name is Dog."
b) 关键字参数
使用 参数名=值 的形式调用函数,这样就不必担心参数的顺序了。
def describe_pet(animal_type, pet_name):
"""显示宠物信息"""
print(f"\nI have a {animal_type}.")
print(f"Its name is {pet_name.title()}.")
# 使用关键字参数调用
describe_pet(pet_name="willie", animal_type="dog")
# 输出:
# I have a dog.
# Its name is Willie.
# 关键字参数和位置参数可以混合使用,但位置参数必须在前面
describe_pet("hamster", "harry")
describe_pet(animal_type="hamster", pet_name="harry")
优点:代码可读性高,不易出错。
c) 默认参数
在定义函数时,为某个参数指定一个默认值,如果调用函数时没有为该参数提供实参,则使用默认值。
def describe_pet(pet_name, animal_type="dog"):
"""显示宠物信息,animal_type 默认为 'dog'"""
print(f"\nI have a {animal_type}.")
print(f"Its name is {pet_name.title()}.")
# 只提供了 pet_name,animal_type 使用默认值 'dog'
describe_pet("willie")
# 输出:
# I have a dog.
# Its name is Willie.
# 提供了所有参数,覆盖了默认值
describe_pet("harry", "hamster")
# 输出:
# I have a hamster.
# Its name is Harry.
重要规则:带默认值的参数必须在不带默认值的参数之后,否则会导致 SyntaxError。
# 错示范例 # def describe_pet(animal_type="dog", pet_name): # 这会报错! # ...
d) 可变长度参数
有时候我们无法预知函数需要接收多少个参数,Python 提供了两种特殊的可变参数来处理这种情况。
*args (接收位置参数,打包成元组)
表示“收集多余的位置参数”,所有被收集的位置参数会被打包成一个元组。
def make_pizza(*toppings):
"""打印顾客点的所有配料"""
print("\nMaking a pizza with the following toppings:")
for topping in toppings:
print(f"- {topping}")
make_pizza("pepperoni")
make_pizza("mushrooms", "green peppers", "extra cheese")
输出:
Making a pizza with the following toppings:
- pepperoni
Making a pizza with the following toppings:
- mushrooms
- green peppers
- extra cheese
在函数内部,toppings 是一个元组,('mushrooms', 'green peppers', 'extra cheese')。
**kwargs (接收关键字参数,打包成字典)
表示“收集多余的关键字参数”,所有被收集的关键字参数会被打包成一个字典。
def build_profile(first, last, **user_info):
"""创建一个字典,其中包含我们知道的有关用户的一切"""
user_info['first_name'] = first
user_info['last_name'] = last
return user_info
user_profile = build_profile('albert', 'einstein',
location='princeton',
field='physics')
print(user_profile)
输出:
{'location': 'princeton', 'field': 'physics', 'first_name': 'albert', 'last_name': 'einstein'}
在函数内部,user_info 是一个字典,{'location': 'princeton', 'field': 'physics'}。
参数传递:传值还是传引用?
这是一个经典问题,在 Python 中,参数传递的方式是“传对象引用”。
-
对于不可变对象 (如
int,str,tuple,bool):函数内部对参数的修改不会影响到外部的原始对象,效果类似于“传值”。def change_value(x): x = 10 # 这里的 x 是一个局部变量,指向了一个新的整数对象 10 my_var = 5 change_value(my_var) print(my_var) # 输出仍然是 5 -
对于可变对象 (如
list,dict,set):函数内部对参数的修改会影响到外部的原始对象,效果类似于“传引用”。def add_item(my_list): my_list.append('new item') # 修改了传入的列表对象 my_list = [1, 2, 3] add_item(my_list) print(my_list) # 输出 [1, 2, 3, 'new item']
记住:Python 中没有真正的“传值”或“传引用”,一切都是“传对象的引用”,根据对象是否可变,表现出不同的行为。
高级用法
a) 仅位置参数
在 Python 3.8+ 中,你可以使用 来指定某些参数必须是位置参数,不能使用关键字传递。
def get_name(first_name, /, last_name, *, age):
"""
first_name: 仅位置参数
last_name: 位置或关键字参数
age: 仅关键字参数
"""
return f"{first_name} {last_name} is {age} years old."
# 正确调用
print(get_name("John", "Doe", age=30))
print(get_name("John", last_name="Doe", age=30))
# 错误调用 (不能对 first_name 使用关键字)
# print(get_name(first_name="John", last_name="Doe", age=30)) # TypeError
b) 仅关键字参数
使用 (单独出现) 或 *args 后面的参数,必须使用关键字形式传递。
def get_info(*, name, age):
"""name 和 age 必须使用关键字参数传递"""
return f"Name: {name}, Age: {age}"
# 正确调用
print(get_info(name="Alice", age=25))
# 错误调用 (不能使用位置参数)
# print(get_info("Alice", 25)) # TypeError
c) 参数解包
有时候你已经有了一个列表或字典,想把它作为参数传递给函数,这时就需要解包。
-
列表或元组解包:使用
def add(a, b, c): return a + b + c my_list = [1, 2, 3] result = add(*my_list) # 等同于 add(1, 2, 3) print(result) # 输出 6 -
字典解包:使用
def introduce(name, age, city): return f"Hello, I'm {name}, {age} years old, from {city}." my_dict = {'name': 'Bob', 'age': 40, 'city': 'New York'} intro = introduce(**my_dict) # 等同于 introduce(name='Bob', age=40, city='New York') print(intro) # 输出 Hello, I'm Bob, 40 years old, from New York.
最佳实践
- 清晰明了的参数名:使用描述性的参数名,如
user_name而不是un。 - 使用默认参数:为可选参数提供有意义的默认值,可以简化函数调用。
- 文档字符串:为你的函数写上文档字符串,说明参数的含义、类型和返回值。
- 可读性优先:在大多数情况下,使用关键字参数来调用函数,尤其是当参数较多或参数名不直观时,这能极大提高代码的可读性。
- *谨慎使用 `args
和kwargs`:它们非常强大,但过度使用可能会让函数接口变得模糊,难以理解,只在确实需要处理可变数量参数时使用。
希望这份详细的解释能帮助你完全理解 Python 函数的参数!
