Skip to content

函数参数详解

掌握参数传递的艺术


参数类型总览

python
def function(
    pos1, pos2,              # 位置参数
    default_arg=10,          # 默认参数
    *args,                   # 可变位置参数
    kwonly_arg,              # 仅限关键字参数
    **kwargs                 # 可变关键字参数
):
    pass

位置参数 vs 关键字参数

python
def describe_person(name, age, city):
    print(f"{name}, {age}岁, 来自{city}")

# 位置参数(顺序重要)
describe_person("Alice", 25, "Beijing")

# 关键字参数(顺序无关)
describe_person(city="Shanghai", name="Bob", age=30)

# 混合(位置参数必须在前)
describe_person("Carol", age=28, city="Guangzhou")

默认参数

python
def calculate_income_tax(income, tax_rate=0.25):
    """默认税率25%"""
    return income * tax_rate

print(calculate_income_tax(100000))         # 25000(用默认值)
print(calculate_income_tax(100000, 0.30))   # 30000(指定税率)

️ 默认参数陷阱

python
#  危险:可变对象作为默认参数
def add_student(name, courses=[]):
    courses.append(name)
    return courses

print(add_student("Alice"))  # ['Alice']
print(add_student("Bob"))    # ['Alice', 'Bob']  ← 共享同一个列表!

#  正确做法
def add_student(name, courses=None):
    if courses is None:
        courses = []
    courses.append(name)
    return courses

可变参数(*args)

python
def calculate_average(*scores):
    """接受任意数量的分数"""
    if not scores:
        return 0
    return sum(scores) / len(scores)

print(calculate_average(85, 90, 78))           # 84.33
print(calculate_average(95, 88, 92, 76, 85))   # 87.2

# 解包列表
my_scores = [85, 90, 78, 92]
print(calculate_average(*my_scores))  # 使用*解包

实用案例:合并数据

python
def merge_survey_data(*surveys):
    """合并多个调查数据"""
    all_respondents = []
    for survey in surveys:
        all_respondents.extend(survey)
    return all_respondents

survey1 = [{"id": 1, "age": 25}, {"id": 2, "age": 30}]
survey2 = [{"id": 3, "age": 35}]
survey3 = [{"id": 4, "age": 40}, {"id": 5, "age": 45}]

all_data = merge_survey_data(survey1, survey2, survey3)
print(f"总受访者: {len(all_data)} 人")

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

python
def create_respondent(**info):
    """创建受访者记录"""
    return {
        'respondent_id': info.get('id', 0),
        'name': info.get('name', 'Unknown'),
        'age': info.get('age', 0),
        'additional_info': {k: v for k, v in info.items()
                           if k not in ['id', 'name', 'age']}
    }

# 灵活传参
resp = create_respondent(
    id=1001,
    name="Alice",
    age=30,
    income=75000,
    education="Master's",
    city="Beijing"
)
print(resp)

解包字典

python
def register_student(name, age, major):
    print(f"{name}, {age}岁, {major}")

student_data = {'name': 'Alice', 'age': 25, 'major': 'Economics'}
register_student(**student_data)  # 解包字典

组合使用

python
def process_survey_data(survey_name, year, *respondents, **metadata):
    """
    survey_name, year: 必须的位置参数
    *respondents: 可变数量的受访者
    **metadata: 额外的元数据
    """
    print(f"=== {survey_name} ({year}) ===")
    print(f"受访者数量: {len(respondents)}")
    print(f"元数据: {metadata}")

    for i, resp in enumerate(respondents, 1):
        print(f"  {i}. {resp}")

# 使用
process_survey_data(
    "中国居民收入调查",
    2024,
    {"id": 1, "age": 30},
    {"id": 2, "age": 35},
    {"id": 3, "age": 40},
    region="全国",
    sample_method="分层抽样",
    response_rate=0.85
)

仅限关键字参数(Python 3+)

python
def calculate_tax(income, *, tax_rate, deduction=0):
    """
    income: 位置参数
    *: 之后的参数必须用关键字传递
    tax_rate: 必须用关键字
    deduction: 有默认值,但也必须用关键字
    """
    taxable = income - deduction
    return taxable * tax_rate

#  正确
result = calculate_tax(100000, tax_rate=0.25, deduction=10000)

#  错误
# result = calculate_tax(100000, 0.25)  # TypeError

为什么使用? 提高代码可读性,避免参数位置错误。


实战:数据分析函数库

python
def filter_data(data, *, min_age=None, max_age=None, gender=None, min_income=None):
    """筛选数据(仅限关键字参数保证清晰)"""
    filtered = data

    if min_age is not None:
        filtered = [r for r in filtered if r.get('age', 0) >= min_age]

    if max_age is not None:
        filtered = [r for r in filtered if r.get('age', 0) <= max_age]

    if gender is not None:
        filtered = [r for r in filtered if r.get('gender') == gender]

    if min_income is not None:
        filtered = [r for r in filtered if r.get('income', 0) >= min_income]

    return filtered

# 使用
respondents = [
    {'id': 1, 'age': 25, 'gender': 'Female', 'income': 50000},
    {'id': 2, 'age': 35, 'gender': 'Male', 'income': 80000},
    {'id': 3, 'age': 45, 'gender': 'Female', 'income': 95000},
]

# 清晰的筛选
young_females = filter_data(respondents, max_age=30, gender='Female')
high_earners = filter_data(respondents, min_income=70000)

最佳实践

1. 参数顺序

python
# 推荐顺序
def function(
    required_positional,      # 必须的位置参数
    optional_positional=None, # 可选的位置参数
    *args,                    # 可变位置参数
    required_keyword_only,    # 必须的关键字参数
    optional_keyword=None,    # 可选的关键字参数
    **kwargs                  # 可变关键字参数
):
    pass

2. 何时使用各种参数

参数类型使用场景
位置参数参数少且明显(如 calculate_bmi(weight, height)
默认参数常用默认值(如 tax_rate=0.25
*args数量不定的同类参数(如 sum(*numbers)
**kwargs灵活的配置选项
关键字专用提高可读性,避免混淆

3. 函数签名要清晰

python
#  不清晰
def process(a, b, c, d, e, f):
    pass

#  清晰
def process_survey_response(
    respondent_id,
    age,
    gender,
    income,
    education,
    satisfaction_score
):
    pass

练习题

练习 1:灵活的统计函数

python
# 编写函数 calculate_stats(*values, **options)
# values: 任意数量的数值
# options: 可选参数
#   - include_std: 是否计算标准差
#   - round_digits: 保留小数位数

# 示例
result = calculate_stats(1, 2, 3, 4, 5, include_std=True, round_digits=2)

练习 2:数据筛选器

python
# 编写函数 filter_respondents(data, **criteria)
# criteria 可以包含: min_age, max_age, gender, education, city 等
# 返回符合所有条件的受访者列表

data = [
    {'id': 1, 'age': 25, 'gender': 'F', 'city': 'Beijing'},
    {'id': 2, 'age': 35, 'gender': 'M', 'city': 'Shanghai'},
    # ...
]

result = filter_respondents(data, min_age=30, city='Shanghai')

下一步

在下一节中,我们将学习 Lambda 函数和函数式编程

继续!

基于 MIT 许可证发布。内容版权归作者所有。