Python前缀运算符 * 和 ** 用法详解:从函数参数到序列解包
本文详解Python中前缀运算符*和**的多种用法。涵盖函数定义时捕获位置与关键字参数、函数调用时解包列表与字典、仅关键字参数的限制,以及Python 3.5+在列表、元组、集合和字典字面量中合并可迭代对象的高级技巧,助力写出更简洁高效的代码。
Python前缀运算符 * 和 ** 概述
本文主要探讨Python中前缀运算符*和**的具体用法,解析它们在变量前的不同行为。
单个星号(*):表示接收到的位置参数将作为元组进行处理。
双个星号(**):表示接收到的关键字参数将作为字典进行处理。
以下是简单示例:
>>> numbers = [2, 1, 3, 4, 7]
>>> more_numbers = [*numbers, 11, 18]
>>> print(*more_numbers, sep=', ')
2, 1, 3, 4, 7, 11, 18
* 和 ** 在函数调用与定义中的应用
主要用途包括:
- 在函数调用中使用*和**传递参数。
- 使用*和**捕获传递给函数的参数。
- 利用*仅接受关键字参数。
- 在元组拆包过程中捕获项目。
- 使用*将可迭代对象解包到列表或元组中。
- 使用**将字典解包到其他字典中。
函数调用时的参数解包
以下是对上述例子的详细解释:
1. 在调用函数时,可以使用*运算符将可迭代对象解压缩为函数调用中的独立参数:
>>> fruits = ['lemon', 'pear', 'watermelon', 'tomato']
>>> print(fruits[0], fruits[1], fruits[2], fruits[3])
lemon pear watermelon tomato
>>> print(*fruits)
lemon pear watermelon tomato
该print(*fruits)语句将fruits列表中的所有项目作为单独的参数传递给print函数,即使我们事先不知道列表中有多少个元素。
2. **运算符允许我们处理包含键值对的字典,并将其解压缩为函数调用中的关键字参数。
>>> date_info = {'year': "2020", 'month': "01", 'day': "01"}
>>> filename = "{year}-{month}-{day}.txt".format(**date_info)
>>> filename
'2020-01-01.txt'
虽然将关键字参数解包到函数调用中并不常见,但在处理继承时经常看到:super()通常同时包含*和**。此外,*和**可以在函数调用中多次使用,例如在Python 3.5及更高版本中。
>> fruits = ['lemon', 'pear', 'watermelon', 'tomato']
>>> numbers = [2, 1, 3, 4, 7]
>>> print(*numbers, *fruits)
2 1 3 4 7 lemon pear watermelon tomato
**多次使用类似:
>>> date_info = {'year': "2020", 'month': "01", 'day': "01"}
>>> track_info = {'artist': "Beethoven", 'title': 'Symphony No 5'}
>>> filename = "{year}-{month}-{day}-{artist}-{title}.txt".format(
... **date_info,
... **track_info,
... )
>>> filename
'2020-01-01-Beethoven-Symphony No 5.txt'
函数定义时的参数捕获
3. 在定义函数时,可以使用*运算符捕获无限数量的位置参数。这些参数会被收集到一个元组中。
from random import randint
def roll(*dice):
return sum(randint(1, die) for die in dice
4. 在定义函数时,我们可以用**来捕捉传递给函数的任何关键字参数,并将它们存储在字典中:
def tag(tag_name, **attributes):
attribute_list = [
f'{name}="{value}"'
for name, value in attributes.items()
]
return f"<{tag_name} {' '.join(attribute_list)}>"
5. 带有仅关键字参数的位置参数:若要接受仅关键字的参数,可以在定义函数时将命名参数放置在*之后。
def get_multiple(*keys, dictionary, default=None):
return [
dictionary.get(key, default)
for key in keys
]
上面的函数可以这样使用:
>>> fruits = {'lemon': 'yellow', 'orange': 'orange', 'tomato': 'red'}
>>> get_multiple('lemon', 'tomato', 'squash', dictionary=fruits, default='unknown')
['yellow', 'red', 'unknown']
参数dictionary和default位于*之后,这意味着只能将它们指定为关键字参数。如果我们尝试在位置上指定它们,则会收到错误消息:
>>> fruits = {'lemon': 'yellow', 'orange': 'orange', 'tomato': 'red'}
>>> get_multiple('lemon', 'tomato', 'squash', fruits, 'unknown')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: get_multiple() missing 1 required keyword-only argument: 'dictionary'
6. 不带位置参数的仅关键字参数 仅关键字参数的功能很强大,但如果您需要仅关键字参数而不捕获无限的位置参数该怎么办?
def with_previous(iterable, *, fillvalue=None):
"""Yield each iterable item along with the item before it."""
previous = fillvalue
for item in iterable:
yield previous, item
previous = item
```
该函数接受一个iterable参数,该参数可以在位置上指定(作为第一个参数),也可以通过其名称和作为fillvalue仅关键字参数的参数来指定。这意味着我们可以这样调用with_previous:
>>> list(with_previous([2, 1, 3], fillvalue=0))
[(0, 2), (2, 1), (1, 3)]
但不是这样的:
>>> list(with_previous([2, 1, 3], 0))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: with_previous() takes 1 positional argument but 2 were given
此函数接受两个参数,并且其中一个fillvalue 必须指定为关键字arguments。
元组拆包与序列解包技巧
7. 元组拆包中的星号
Python 3还添加了一种新的使用*运算符的方式,该方式仅与上述定义函数时的*和调用函数时的*功能有关。
>>> fruits = ['lemon', 'pear', 'watermelon', 'tomato']
>>> first, second, *remaining = fruits
>>> remaining
['watermelon', 'tomato']
>>> first, *remaining = fruits
>>> remaining
['pear', 'watermelon', 'tomato']
>>> first, *middle, last = fruits
>>> middle
['pear', 'watermelon']
Python 3.5+ 序列与字典字面量中的解包
8. 列表字面量中的星号 Python 3.5通过PEP 448引入了大量新功能。最大的新功能之一是能够将可迭代对象转储到新列表中。
假设您有一个函数,该函数可以接收任何序列,并返回一个列表,其中该序列与该序列的反序连接在一起:
def palindromify(sequence):
return list(sequence) + list(reversed(sequence))
该函数需要将事物转换为列表几次,以连接列表并返回结果。在Python 3.5中,我们可以改为输入:
def palindromify(sequence):
return [*sequence, *reversed(sequence)]
此代码删除了一些不必要的list调用,因此我们的代码更加高效和可读。
这是另一个例子:
def rotate_first_item(sequence):
return [*sequence[1:], sequence[0]]
该函数返回一个新列表,其中给定列表(或其他序列)中的第一项移动到新列表的末尾。
*运算符的这种使用是将不同类型的可迭代对象连接在一起的好方法。*操作符适用于任何可迭代对象,而使用+操作符仅适用于具有所有相同类型的特定序列。
这不仅限于创建列表。我们还可以将可迭代项转储到新的元组或集合中:
>>> fruits = ['lemon', 'pear', 'watermelon', 'tomato']
>>> (*fruits[1:], fruits[0])
('pear', 'watermelon', 'tomato', 'lemon')
>>> uppercase_fruits = (f.upper() for f in fruits)
>>> {*fruits, *uppercase_fruits}
{'lemon', 'watermelon', 'TOMATO', 'LEMON', 'PEAR', 'WATERMELON', 'tomato', 'pear'}
请注意,上面的最后一行获取一个列表和一个生成器,并将它们转储到新集中。在使用*之前,以前没有一种简单的方法可以在一行代码中做到这一点。以前有一种方法可以做到,但要记住或发现它并不容易:
>>> set().union(fruits, uppercase_fruits)
{'lemon', 'watermelon', 'TOMATO', 'LEMON', 'PEAR', 'WATERMELON', 'tomato', 'pear'}
9. 字典字面量中的双星号 PEP 448还允许**运算符用于将键/值对从一个字典转储到新字典中,从而扩展了功能:
>>> date_info = {'year': "2020", 'month': "01", 'day': "01"}
>>> track_info = {'artist': "Beethoven", 'title': 'Symphony No 5'}
>>> all_info = {**date_info, **track_info}
>>> all_info
{'year': '2020', 'month': '01', 'day': '01', 'artist': 'Beethoven', 'title': 'Symphony No 5'}
例如,我们可以在添加新值的同时复制字典:
>>> date_info = {'year': '2020', 'month': '01', 'day': '7'}
>>> event_info = {**date_info, 'group': "Python Meetup"}
>>> event_info
{'year': '2020', 'month': '01', 'day': '7', 'group': 'Python Meetup'}
或在覆盖特定值的同时复制/合并字典:
>>> event_info = {'year': '2020', 'month': '01', 'day': '7', 'group': 'Python Meetup'}
>>> new_info = {**event_info, 'day': "14"}
>>> new_info
{'year': '2020', 'month': '01', 'day': '14', 'group': 'Python Meetup'}
参考链接: https://treyhunner.com/2018/10/asterisks-in-python-what-they-are-and-how-to-use-them/
总结
到此这篇关于Python中前缀运算符*和**的用法示例详解的文章就介绍到这了,更多相关Python中*和**的用法内容请搜索本站以前的文章或继续浏览下面的相关文章,希望大家以后多多支持本站!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













