Python 函数传参方式

Python函数传参方式详解

📊 参数传递方式概览

传参方式语法特点使用场景
位置参数func(a, b, c)按顺序传递常规参数传递
关键字参数func(a=1, b=2)按参数名传递提高可读性,避免顺序错误
默认参数func(a, b=10)参数默认值可选参数
可变位置参数func(*args)接收任意数量的位置参数参数数量不确定
可变关键字参数func(**kwargs)接收任意数量的关键字参数参数键值对不确定
仅限位置参数func(a, b, /)只能通过位置传递Python 3.8+,确保API稳定性
仅限关键字参数func(*, a, b)只能通过关键字传递明确参数意图,避免混淆

🔧 基础传参方式

1. 位置参数(Positional Arguments)

def greet(name, greeting, punctuation):
"""按位置顺序传递参数"""
print(f"{greeting}, {name}{punctuation}")

# 必须按定义顺序传递参数
greet("Alice", "Hello", "!")
greet("Bob", "Hi", ".")

# 错误示例:顺序错误
# greet("Hello", "Alice", "!") # 逻辑错误:Hello被当作name

2. 关键字参数(Keyword Arguments)

def create_user(username, email, age, is_active=True):
"""使用参数名传递值"""
user = {
"username": username,
"email": email,
"age": age,
"is_active": is_active
}
return user

# 使用关键字参数(顺序不重要)
user1 = create_user(
username="alice123",
email="alice@example.com",
age=25,
is_active=True
)

# 混合使用(位置参数必须在关键字参数之前)
user2 = create_user(
"bob456", # 位置参数
age=30, # 关键字参数(顺序可变)
email="bob@example.com",
is_active=False
)

print(user1)
print(user2)

3. 默认参数(Default Arguments)

def connect_database(
host="localhost",
port=5432,
user="admin",
password="",
database="test"
):
"""带默认值的参数"""
connection_string = f"host={host} port={port} user={user}"
if password:
connection_string += f" password={password}"
connection_string += f" database={database}"
return connection_string

# 使用默认值
print(connect_database()) # 使用所有默认值

# 覆盖部分默认值
print(connect_database(host="192.168.1.100", database="production"))

# 注意:默认参数在函数定义时计算一次!
def add_item(item, items=[]):
"""危险!默认参数共享同一个列表"""
items.append(item)
return items

print(add_item("apple")) # ['apple']
print(add_item("banana")) # ['apple', 'banana'] - 同一个列表!

# 正确做法:使用None作为默认值
def add_item_safe(item, items=None):
if items is None:
items = []
items.append(item)
return items

print(add_item_safe("apple")) # ['apple']
print(add_item_safe("banana")) # ['banana'] - 新列表

🌟 高级传参方式

4. 可变位置参数(*args)

def calculate_average(*args):
"""接收任意数量的位置参数"""
if not args:
return 0
return sum(args) / len(args)

# 可以传递任意数量的参数
print(calculate_average(1, 2, 3)) # 2.0
print(calculate_average(10, 20, 30, 40)) # 25.0
print(calculate_average()) # 0

# *args收集所有位置参数为一个元组
def show_args(*args):
print(f"args类型: {type(args)}")
print(f"args值: {args}")
for i, arg in enumerate(args, 1):
print(f"参数{i}: {arg}")

show_args(1, "hello", [1, 2, 3], {"key": "value"})

# 与其他参数结合
def make_pizza(size, *toppings):
print(f"制作一个{size}尺寸的披萨,配料:")
for topping in toppings:
print(f" - {topping}")

make_pizza("大号", "芝士", "意大利辣香肠", "蘑菇", "洋葱")

# 解包列表/元组作为参数
numbers = [1, 2, 3, 4, 5]
print(calculate_average(*numbers)) # 使用*解包

5. 可变关键字参数(**kwargs)

def build_profile(**kwargs):
"""接收任意数量的关键字参数"""
profile = {}
for key, value in kwargs.items():
profile[key] = value
return profile

# 可以传递任意数量的关键字参数
user_profile = build_profile(
name="Alice",
age=30,
city="New York",
job="Engineer",
hobbies=["reading", "hiking"]
)

print(user_profile)

# **kwargs收集所有关键字参数为一个字典
def show_kwargs(**kwargs):
print(f"kwargs类型: {type(kwargs)}")
for key, value in kwargs.items():
print(f"{key}: {value}")

show_kwargs(name="Bob", age=25, country="USA")

# 解包字典作为参数
config = {"host": "localhost", "port": 8080, "debug": True}
def configure_server(host, port, debug=False):
print(f"服务器配置: host={host}, port={port}, debug={debug}")

configure_server(**config) # 使用**解包字典

6. 综合使用所有参数类型

def comprehensive_function(a, b=10, *args, c=20, d=30, **kwargs):
"""
参数顺序规则:
1. 位置参数
2. 默认参数
3. *args
4. 关键字参数(带默认值)
5. **kwargs
"""
print(f"a={a}, b={b}")
print(f"args={args}")
print(f"c={c}, d={d}")
print(f"kwargs={kwargs}")
print("-" * 30)

# 各种调用方式
comprehensive_function(1) # 只传递必需参数
comprehensive_function(1, 2) # 覆盖b的默认值
comprehensive_function(1, 2, 3, 4, 5) # 额外参数给*args
comprehensive_function(1, 2, 3, 4, c=50, d=60) # 覆盖c和d
comprehensive_function(1, 2, 3, 4, e=70, f=80) # 额外参数给**kwargs

# 完整示例
def create_report(title, *data_sources, format="pdf", **metadata):
"""创建报告的函数"""
report = {
"title": title,
"data_sources": data_sources,
"format": format,
"metadata": metadata
}
return report

report = create_report(
"季度销售报告",
"sales_q1.csv",
"sales_q2.csv",
"sales_q3.csv",
format="excel",
author="Alice",
department="Sales",
date="2024-01-15"
)

print(report)

🆕 Python 3.8+ 新增传参方式

7. 仅限位置参数(Positional-Only Parameters)

# 使用/指定仅限位置参数
def power(x, y, /):
"""x和y只能通过位置传递,不能使用关键字"""
return x ** y

# 正确:使用位置参数
print(power(2, 3)) # 8

# 错误:不能使用关键字参数
# print(power(x=2, y=3)) # TypeError

# 混合使用
def connect(host, port, /, timeout=30, *, secure=True):
"""
host, port: 仅限位置参数
timeout: 位置或关键字参数(在/之后)
secure: 仅限关键字参数(在*之后)
"""
print(f"连接到 {host}:{port}")
print(f"超时: {timeout}秒")
print(f"安全连接: {secure}")

connect("example.com", 8080) # 使用默认值
connect("example.com", 8080, timeout=60) # 位置传递timeout
connect("example.com", 8080, timeout=60, secure=False) # 关键字传递secure
# connect(host="example.com", port=8080) # 错误:host和port是仅限位置的

8. 仅限关键字参数(Keyword-Only Parameters)

# 使用*指定仅限关键字参数
def register_user(username, *, email, phone):
"""email和phone只能通过关键字传递"""
user_info = {
"username": username,
"email": email,
"phone": phone
}
return user_info

# 正确:username位置传递,email和phone关键字传递
user = register_user("alice123", email="alice@example.com", phone="123-456-7890")

# 错误:email和phone不能位置传递
# user = register_user("alice123", "alice@example.com", "123-456-7890") # TypeError

# 与*args结合
def process_items(*items, reverse=False, unique=False):
"""
items: 可变位置参数
reverse, unique: 仅限关键字参数
"""
if unique:
items = list(set(items))

result = list(items)
if reverse:
result.reverse()

return result

print(process_items(1, 2, 3, 2, 1)) # [1, 2, 3, 2, 1]
print(process_items(1, 2, 3, 2, 1, unique=True)) # [1, 2, 3]
print(process_items(1, 2, 3, reverse=True)) # [3, 2, 1]

🔄 参数解包技巧

解包列表/元组作为位置参数

def print_coordinates(x, y, z):
print(f"坐标: ({x}, {y}, {z})")

# 使用*解包序列
point = [10, 20, 30]
print_coordinates(*point) # 等价于 print_coordinates(10, 20, 30)

# 解包部分参数
def print_person(name, age, city):
print(f"{name}, {age}岁, 来自{city}")

info = ["Alice", 30]
print_person(*info, "New York") # Alice, 30岁, 来自New York

解包字典作为关键字参数

def create_product(name, price, category, in_stock=True):
return {
"name": name,
"price": price,
"category": category,
"in_stock": in_stock
}

# 使用**解包字典
product_data = {
"name": "Laptop",
"price": 999.99,
"category": "Electronics",
"in_stock": False
}

product = create_product(**product_data)
print(product)

混合解包

def complex_function(a, b, c, d, e, f):
print(f"a={a}, b={b}, c={c}, d={d}, e={e}, f={f}")

args_list = [1, 2]
args_tuple = (3, 4)
kwargs_dict = {"e": 5, "f": 6}

# 混合解包
complex_function(*args_list, *args_tuple, **kwargs_dict) # a=1, b=2, c=3, d=4, e=5, f=6

🎯 实际应用示例

配置处理函数

def configure_app(**options):
"""灵活的应用程序配置"""
defaults = {
"debug": False,
"host": "localhost",
"port": 8000,
"workers": 4,
"log_level": "INFO"
}

# 合并默认配置和用户配置
config = {**defaults, **options}

# 验证配置
if not 0 < config["port"] <= 65535:
raise ValueError("端口必须在1-65535之间")

return config

# 使用不同配置
dev_config = configure_app(debug=True, port=8080)
prod_config = configure_app(host="0.0.0.0", workers=8, log_level="WARNING")

print("开发配置:", dev_config)
print("生产配置:", prod_config)

数据验证装饰器

def validate_arguments(*validators):
"""参数验证装饰器"""
def decorator(func):
def wrapper(*args, **kwargs):
# 验证位置参数
for i, (arg, validator) in enumerate(zip(args, validators)):
if not validator(arg):
raise ValueError(f"参数{i+1}验证失败: {arg}")

# 这里可以扩展验证关键字参数
return func(*args, **kwargs)
return wrapper
return decorator

# 使用验证器
@validate_arguments(lambda x: x > 0, lambda x: isinstance(x, str))
def divide_and_print(number, text):
result = 100 / number
print(f"{text}: {result}")

divide_and_print(10, "结果") # 正常执行
# divide_and_print(-5, "结果") # 验证失败
# divide_and_print(10, 123) # 验证失败

函数柯里化(部分应用)

from functools import partial

def power(base, exponent):
"""计算幂"""
return base ** exponent

# 创建平方函数
square = partial(power, exponent=2)
# 创建立方函数
cube = partial(power, exponent=3)

print(square(5)) # 25 (5²)
print(cube(5)) # 125 (5³)

# 自定义柯里化函数
def curry(func):
"""将函数柯里化"""
def curried(*args, **kwargs):
if len(args) + len(kwargs) >= func.__code__.co_argcount:
return func(*args, **kwargs)
else:
return lambda *more_args, **more_kwargs: curried(
*args, *more_args, **{**kwargs, **more_kwargs}
)
return curried

# 使用柯里化
@curry
def add_three_numbers(a, b, c):
return a + b + c

add_5 = add_three_numbers(5) # 固定第一个参数为5
add_5_and_10 = add_5(10) # 固定第二个参数为10
result = add_5_and_10(15) # 15 + 10 + 5 = 30
print(result)

⚠️ 常见陷阱与解决方案

陷阱1:可变默认参数

# 错误示例
def append_to_list(value, my_list=[]):
my_list.append(value)
return my_list

print(append_to_list(1)) # [1]
print(append_to_list(2)) # [1, 2] - 同一个列表!

# 解决方案:使用None
def append_to_list_correct(value, my_list=None):
if my_list is None:
my_list = []
my_list.append(value)
return my_list

print(append_to_list_correct(1)) # [1]
print(append_to_list_correct(2)) # [2] - 新列表

陷阱2:关键字参数顺序

def process_data(a, b=1, *args, c=2, d=3, **kwargs):
pass

# 正确
process_data(10, 20, 30, 40, c=50, e=60)

# 错误:关键字参数在位置参数之前
# process_data(10, c=50, 20, 30, 40) # SyntaxError

陷阱3:解包时键不匹配

def show_info(name, age):
print(f"{name} is {age} years old")

user_data = {"name": "Alice", "age": 30, "city": "New York"}

# 正确:多余的键被忽略(Python 3.6+)
show_info(**user_data)

# 错误:缺少必需的键
# missing_data = {"name": "Bob"}
# show_info(**missing_data) # TypeError

陷阱4:参数遮蔽

def bad_example(list, dict):  # 使用内置函数名作为参数名
return list + list(dict.keys()) # 现在list是参数,不是内置函数

# 这会工作,但会导致混淆
result = bad_example([1, 2], {"a": 1, "b": 2})
print(result) # [1, 2, 'a', 'b']

# 更好的做法:使用有意义的名称
def good_example(items, mapping):
return items + list(mapping.keys())

💡 最佳实践

  1. 参数顺序:位置参数 → 默认参数 → args → 仅限关键字参数 → *kwargs
  2. 命名明确:使用有意义的参数名,避免使用内置函数名
  3. 默认值安全:对于可变对象,使用None作为默认值
  4. 类型提示:使用类型注解提高代码可读性
  5. 文档注释:为复杂参数编写清晰的文档字符串
  6. 参数验证:在函数开始时验证参数有效性
  7. 保持简洁:避免过多的参数,考虑使用字典或对象
  8. 使用新特性:在适当场景使用仅限位置/关键字参数

掌握Python函数传参的各种方式,可以让你编写更加灵活、健壮和易用的函数接口!