python中函数参数前的星号(*)是什么意思?

冰火之心
发布: 2025-09-12 16:54:02
原创
1307人浏览过
星号()在Python函数中主要用于参数收集、解包和强制关键字参数。在函数定义时,args将位置参数打包为元组,kwargs将关键字参数打包为字典;在函数调用时,可迭代对象将其元素解包为位置参数,字典将其键值对解包为关键字参数;此外,单独的可作为分隔符,强制其后的参数必须以关键字形式传递,提升代码可读性和API设计清晰度。

python中函数参数前的星号(*)是什么意思?

在Python函数参数前看到星号(

*
登录后复制
),它通常意味着两种核心功能:一是收集不确定数量的位置参数,将它们打包成一个元组;二是在函数调用时,将一个可迭代对象解包成独立的参数。此外,它还能用于强制后续参数必须以关键字形式传递,这在设计API时特别有用。

解决方案

星号(

*
登录后复制
)在Python函数参数中的用法,其实可以分为两大类:参数收集(Packing)参数解包(Unpacking),以及一个特殊的关键字参数强制用途。理解这三点,就基本掌握了它的核心奥秘。

1. 参数收集(Packing)

当你在函数定义时使用一个星号(

*
登录后复制
)或两个星号(
**
登录后复制
)时,它们的作用是将传入的多个参数“打包”成一个单一的变量。

立即学习Python免费学习笔记(深入)”;

  • *`args`:收集位置参数**

    • 当你在函数定义中看到

      def my_function(*args):
      登录后复制
      时,这意味着
      my_function
      登录后复制
      可以接受任意数量的位置参数。这些参数会被收集到一个元组(tuple)中,并赋值给
      args
      登录后复制
      这个变量(
      args
      登录后复制
      只是一个惯例名称,你可以用其他名字,比如
      *items
      登录后复制
      )。

    • 例子:

      def calculate_sum(*numbers):
          print(f"收到的参数类型是:{type(numbers)}")
          total = 0
          for num in numbers:
              total += num
          return total
      
      print(calculate_sum(1, 2, 3))       # 输出:收到的参数类型是:<class 'tuple'>, 6
      print(calculate_sum(10, 20, 30, 40)) # 输出:收到的参数类型是:<class 'tuple'>, 100
      print(calculate_sum())             # 输出:收到的参数类型是:<class 'tuple'>, 0
      登录后复制
    • 在我看来,这极大地增强了函数的灵活性,尤其是在你不知道调用者会传入多少个参数时,比如一个简单的求和函数或者一个日志记录器。

  • `kwargs`:收集关键字参数**

    • 类似地,当你在函数定义中看到

      def my_function(**kwargs):
      登录后复制
      时,它允许函数接受任意数量的关键字参数。这些参数会被收集到一个字典(dictionary)中,并赋值给
      kwargs
      登录后复制
      这个变量(
      kwargs
      登录后复制
      也是惯例名称,比如
      **options
      登录后复制
      )。

    • 例子:

      def display_info(**details):
          print(f"收到的参数类型是:{type(details)}")
          for key, value in details.items():
              print(f"{key}: {value}")
      
      display_info(name="Alice", age=30, city="New York")
      # 输出:
      # 收到的参数类型是:<class 'dict'>
      # name: Alice
      # age: 30
      # city: New York
      
      display_info(product="Laptop", price=1200)
      # 输出:
      # 收到的参数类型是:<class 'dict'>
      # product: Laptop
      # price: 1200
      登录后复制
    • 这种模式在配置函数或构建灵活的API时非常常见,例如,Django ORM中的

      filter()
      登录后复制
      方法就大量使用了
      **kwargs
      登录后复制
      来处理各种查询条件。

2. 参数解包(Unpacking)

当你在函数调用时使用一个星号(

*
登录后复制
)或两个星号(
**
登录后复制
)时,它们的作用是将一个可迭代对象(如列表、元组)或一个字典“解包”成独立的参数。

  • *`iterable`:解包可迭代对象**

    • 如果你有一个列表或元组,并且想将它的每个元素作为独立的参数传递给函数,你可以在变量前加上一个星号。

    • 例子:

      def greet(name1, name2, name3):
          print(f"Hello {name1}, {name2}, and {name3}!")
      
      names = ["Alice", "Bob", "Charlie"]
      greet(*names) # 等同于 greet("Alice", "Bob", "Charlie")
      # 输出:Hello Alice, Bob, and Charlie!
      
      # 另一个常见的例子是与内置函数结合
      numbers = [10, 20, 5]
      print(max(*numbers)) # 等同于 max(10, 20, 5), 输出:20
      登录后复制
    • 这对于我来说,是代码简洁性的一个巨大提升,避免了手动索引和传递每个元素,特别是在参数数量不固定时。

  • `dictionary`:解包字典**

    阿里云-虚拟数字人
    阿里云-虚拟数字人

    阿里云-虚拟数字人是什么? ...

    阿里云-虚拟数字人 2
    查看详情 阿里云-虚拟数字人
    • 如果你有一个字典,并且想将它的键值对作为关键字参数传递给函数,你可以在字典变量前加上两个星号。字典的键会成为参数名,值会成为参数值。

    • 例子:

      def configure_printer(model, dpi, color_mode):
          print(f"Configuring {model}: DPI={dpi}, Color Mode={color_mode}")
      
      printer_settings = {"model": "HP LaserJet", "dpi": 600, "color_mode": "Grayscale"}
      configure_printer(**printer_settings)
      # 输出:Configuring HP LaserJet: DPI=600, Color Mode=Grayscale
      
      # 结合参数收集和解包
      def create_user(username, email, **profile_data):
          print(f"Creating user: {username}, Email: {email}")
          for key, value in profile_data.items():
              print(f"  {key}: {value}")
      
      user_info = {"username": "john_doe", "email": "john@example.com", "age": 30, "city": "London"}
      create_user(**user_info) # 注意这里,username和email会被提取,剩下的进入profile_data
      # 输出:
      # Creating user: john_doe, Email: john@example.com
      #   age: 30
      #   city: London
      登录后复制
    • 这种解包方式在处理配置字典或者将一个函数的结果作为另一个函数的输入时非常方便。

3. 强制关键字参数

在函数定义中,单个星号(

*
登录后复制
)还可以作为位置参数和关键字参数之间的分隔符。在
*
登录后复制
之后定义的任何参数都必须以关键字形式传递,而不能作为位置参数。

  • 例子:

    def send_email(to, subject, *, body, attachments=None):
        print(f"To: {to}")
        print(f"Subject: {subject}")
        print(f"Body: {body}")
        if attachments:
            print(f"Attachments: {', '.join(attachments)}")
    
    send_email("user@example.com", "Meeting Reminder", body="Don't forget the meeting!")
    # 输出:
    # To: user@example.com
    # Subject: Meeting Reminder
    # Body: Don't forget the meeting!
    
    # send_email("user@example.com", "Meeting Reminder", "Don't forget the meeting!")
    # 这会报错:TypeError: send_email() takes 2 positional arguments but 3 were given
    登录后复制
  • 这种用法在我看来,对于提高代码的可读性和防止调用者误用参数至关重要。它强制调用者明确参数的意图,尤其是在函数有多个参数且某些参数的顺序不那么直观时。

Python函数定义中,
*args
登录后复制
**kwargs
登录后复制
具体是如何工作的?

*args
登录后复制
**kwargs
登录后复制
是Python中处理不确定数量函数参数的强大机制,它们的核心工作原理在于“收集”和“打包”。

当你在函数签名中看到

*args
登录后复制
时,Python解释器会将其视为一个指令:将所有在
*args
登录后复制
之前未被明确匹配的位置参数,按照它们传入的顺序,打包成一个元组(tuple)。这个元组会赋给
args
登录后复制
这个变量名。这意味着,即使没有额外的参数传入,
args
登录后复制
也会是一个空元组;如果有参数传入,它们就会按顺序填充这个元组。例如,
def func(a, b, *args):
登录后复制
,如果你调用
func(1, 2, 3, 4, 5)
登录后复制
,那么
a
登录后复制
是1,
b
登录后复制
是2,而
args
登录后复制
会是
(3, 4, 5)
登录后复制

类似地,

**kwargs
登录后复制
处理的是关键字参数。当函数签名中包含
**kwargs
登录后复制
时,所有在
**kwargs
登录后复制
之前未被明确匹配的关键字参数,都会被收集起来,打包成一个字典(dictionary)。这个字典的键是参数名,值是对应的参数值。这个字典会赋给
kwargs
登录后复制
这个变量名。举个例子,
def func(x, **kwargs):
登录后复制
,如果你调用
func(10, name="Alice", age=30)
登录后复制
,那么
x
登录后复制
是10,而
kwargs
登录后复制
会是
{'name': 'Alice', 'age': 30}
登录后复制

这种工作方式提供了极大的灵活性,尤其是在编写通用工具函数、装饰器或者需要接受各种配置选项的API时。我经常用它们来构建那些可以根据用户需求动态调整行为的函数,而不需要为每种可能的参数组合都定义一个独立的函数签名。这种模式也使得函数对未来的参数扩展更具弹性,因为它允许在不修改现有函数签名的情况下添加新的可选参数。

在Python函数调用时,如何利用星号(*)高效地解包列表和字典?

在函数调用时,星号(

*
登录后复制
)和双星号(
**
登录后复制
)的用法是“解包”的艺术,它让代码在处理集合数据时显得异常简洁和高效。

当你有一个列表或元组,并且其中的元素恰好对应一个函数所需的位置参数时,你可以使用单个星号(

*
登录后复制
)进行解包。例如,如果
my_list = [1, 2, 3]
登录后复制
,而函数
add(a, b, c)
登录后复制
需要三个位置参数,那么
add(*my_list)
登录后复制
就会将
my_list
登录后复制
中的
1, 2, 3
登录后复制
分别作为
a, b, c
登录后复制
的值传递。这避免了写成
add(my_list[0], my_list[1], my_list[2])
登录后复制
这种繁琐的形式。我发现这在处理来自文件、数据库或网络请求的批量数据时特别有用,因为这些数据常常以列表或元组的形式组织。

# 示例:解包列表作为位置参数
def describe_person(name, age, city):
    print(f"{name} is {age} years old and lives in {city}.")

person_data = ["Jane Doe", 28, "San Francisco"]
describe_person(*person_data)
# 输出:Jane Doe is 28 years old and lives in San Francisco.
登录后复制

对于字典,如果你有一个字典,它的键与函数所需的关键字参数名称匹配,那么你可以使用双星号(

**
登录后复制
)进行解包。例如,如果
my_dict = {'x': 10, 'y': 20}
登录后复制
,而函数
draw_point(x, y)
登录后复制
需要
x
登录后复制
y
登录后复制
两个关键字参数,那么
draw_point(**my_dict)
登录后复制
就会将
my_dict
登录后复制
中的
'x': 10
登录后复制
'y': 20
登录后复制
分别作为
x=10
登录后复制
y=20
登录后复制
传递。这种方式在传递配置信息或从另一个函数返回的字典结果直接作为参数时非常方便。

# 示例:解包字典作为关键字参数
def create_config(host, port=8080, timeout=30):
    print(f"Connecting to {host}:{port} with timeout {timeout}s.")

server_config = {"host": "localhost", "port": 9000}
create_config(**server_config)
# 输出:Connecting to localhost:9000 with timeout 30s.

full_config = {"host": "remote.server.com", "port": 80, "timeout": 60}
create_config(**full_config)
# 输出:Connecting to remote.server.com:80 with timeout 60s.
登录后复制

这种解包机制的“高效”体现在它减少了样板代码,提高了代码的可读性和灵活性。它允许你将数据和函数调用逻辑解耦,使得你可以更专注于数据的组织和函数的行为,而不是如何将数据适配到函数参数上。这在我处理动态参数或需要将一个数据结构映射到函数调用时,是不可或缺的工具。

除了收集参数,单个星号(*)在函数签名中还有哪些特殊用途?

除了我们前面讨论的收集任意数量的位置参数(

*args
登录后复制
)之外,单个星号(
*
登录后复制
)在函数签名中还有一个非常重要的、但有时容易被忽视的特殊用途:强制关键字参数(Keyword-Only Arguments)

当你在函数参数列表中,在一个或多个位置参数之后,或者在

*args
登录后复制
之后,放置一个独立的星号(
*
登录后复制
),那么这个星号之后定义的所有参数都必须以关键字形式传递,而不能作为位置参数。这是一个语法上的分隔符,它明确地告诉Python解释器和函数调用者:从这里开始,后续的参数不再接受位置传递,只能通过名称(关键字)来指定。

# 示例:强制关键字参数
def generate_report(data_source, *, format="csv", destination="email", strict_mode=False):
    """
    生成报告。
    data_source:报告的数据来源(位置参数)。
    format:报告格式(必须是关键字参数)。
    destination:报告发送目的地(必须是关键字参数)。
    strict_mode:是否启用严格模式(必须是关键字参数)。
    """
    print(f"Generating report from {data_source}...")
    print(f"Format: {format}")
    print(f"Destination: {destination}")
    print(f"Strict Mode: {strict_mode}")

# 正确的调用方式
generate_report("database", format="pdf", destination="ftp", strict_mode=True)
# 输出:
# Generating report from database...
# Format: pdf
# Destination: ftp
# Strict Mode: True

generate_report("web_api", format="json") # 使用默认值
# 输出:
# Generating report from web_api...
# Format: json
# Destination: email
# Strict Mode: False

# 错误的调用方式:尝试将 'pdf' 作为位置参数传递给 format
# generate_report("database", "pdf", "ftp")
# 这会引发 TypeError: generate_report() takes 1 positional argument but 3 were given
登录后复制

在我看来,这种强制关键字参数的机制,对于设计清晰、易于理解和维护的API至关重要。它解决了几个实际问题:

  1. 提高可读性与意图明确性: 对于那些具有多个参数的函数,特别是当某些参数的含义不那么直观,或者它们的顺序可能在未来发生变化时,强制关键字参数能让调用者一眼看出每个参数的用途。
    generate_report(data, "pdf", "ftp")
    登录后复制
    就不如
    generate_report(data, format="pdf", destination="ftp")
    登录后复制
    清晰。
  2. 防止参数误用: 它避免了调用者意外地将一个参数的值传递给了错误的参数位置。这种错误在参数类型相同或兼容时尤其难以察觉。
  3. API稳定性: 当你决定在未来调整函数的内部实现,例如改变某个参数的默认值,或者引入新的位置参数时,如果使用了强制关键字参数,你可以更自信地进行这些改动,而不用担心破坏依赖于参数位置的现有代码。
  4. 清晰区分核心参数与可选/配置参数: 通常,核心的、必须的位置参数放在
    *
    登录后复制
    之前,而那些提供额外配置或控制行为的参数则放在
    *
    登录后复制
    之后作为关键字参数。

这种用法让函数签名本身成为了一种文档,它不仅定义了函数可以接受什么,还定义了它应该如何被调用。这对于构建健壮和用户友好的Python库来说,是一个非常有价值的特性。

以上就是python中函数参数前的星号(*)是什么意思?的详细内容,更多请关注php中文网其它相关文章!

python速学教程(入门到精通)
python速学教程(入门到精通)

python怎么学习?python怎么入门?python在哪学?python怎么学才快?不用担心,这里为大家提供了python速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号