杰瑞科技汇

python函数arguments

目录

  1. 基本概念:参数 vs. 参数
  2. 四种参数类型
    • 位置参数
    • 关键字参数
    • 默认参数
    • 可变长度参数 (*args**kwargs)
  3. 参数传递:传值还是传引用?
  4. 高级用法
    • 仅位置参数
    • 仅关键字参数
    • 参数解包
  5. 最佳实践

基本概念:参数 vs. 参数

在学习之前,我们先明确两个容易混淆的术语:

python函数arguments-图1
(图片来源网络,侵删)
  • Parameter (形参): 指在定义函数时,函数名后面括号里的变量,它们是函数接收数据的占位符。

    def my_function(name, age): # name 和 age 就是形参
        print(f"Name: {name}, Age: {age}")
  • Argument (实参): 指在调用函数时,传递给函数的实际值。

    my_function("Alice", 30) # "Alice" 和 30 就是实参

Parameter 是函数的“菜单”,而 Argument 是你点的“菜”。


四种参数类型

Python 函数的参数定义非常灵活,主要分为以下四种。

python函数arguments-图2
(图片来源网络,侵删)

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.

最佳实践

  1. 清晰明了的参数名:使用描述性的参数名,如 user_name 而不是 un
  2. 使用默认参数:为可选参数提供有意义的默认值,可以简化函数调用。
  3. 文档字符串:为你的函数写上文档字符串,说明参数的含义、类型和返回值。
  4. 可读性优先:在大多数情况下,使用关键字参数来调用函数,尤其是当参数较多或参数名不直观时,这能极大提高代码的可读性。
  5. *谨慎使用 `argskwargs`:它们非常强大,但过度使用可能会让函数接口变得模糊,难以理解,只在确实需要处理可变数量参数时使用。

希望这份详细的解释能帮助你完全理解 Python 函数的参数!

分享:
扫描分享到社交APP
上一篇
下一篇