这篇主要探讨 ** 和 * 前缀运算符,**在变量之前使用的*and **运算符.
一个星(*):表示接收的参数作为元组来处理
两个星(**):表示接收的参数作为字典来处理
简单示例:
1 2 3 4 | >>> numbers = [ 2 , 1 , 3 , 4 , 7 ] >>> more_numbers = [ * numbers, 11 , 18 ] >>> print ( * more_numbers, sep = ', ' ) 2 , 1 , 3 , 4 , 7 , 11 , 18 |
用途:
- 使用 * 和 ** 将参数传递给函数
- 使用**和**捕获传递给函数的参数
- 使用*只接受关键字参数
- 使用*元组拆包过程中捕获项目
- 使用*解包iterables到一个列表/元组
- 使用**要解压缩词典到其他字典
例子解释:
1.调用函数时,*可以使用运算符将可迭代对象解压缩为函数调用中的参数:
1 2 3 4 5 | >>> 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.** 运算符允许我们采取键值对的字典,并把它解压到函数调用中的关键字参数。
1 2 3 4 | >>> 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的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | >> 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.定义函数时,*可以使用运算符捕获为函数提供的无限数量的位置参数。这些参数被捕获到一个元组中。
1 2 3 4 | from random import randint def roll( * dice): return sum (randint( 1 , die) for die in dice |
4.我们可以用**定义一个函数时,捕捉给予功能到字典中的任何关键字参数:
1 2 3 4 5 6 | def tag(tag_name, * * attributes): attribute_list = [ f '{name}="{value}"' for name, value in attributes.items() ] return f "<{tag_name} {' '.join(attribute_list)}>" |
5.带有仅关键字参数的位置参数,要接受仅关键字的参数,可以*在定义函数时在使用后放置命名参数
1 2 3 4 5 6 7 8 9 10 | 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' ] |
参数dictionaryand default在其后*keys,这意味着只能将它们指定为关键字参数。如果我们尝试在位置上指定它们,则会收到错误消息:
1 2 3 4 5 | >>> 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.不带位置参数的仅关键字参数
仅关键字参数的功能很酷,但是如果您需要仅关键字参数而不捕获无限的位置参数怎么办?
1 2 3 4 5 6 7 | 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:
1 2 3 4 5 6 7 8 | >>> 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还添加了一种新的使用运算符的方式,该方式仅与上面的-when-defining-a-function和*-when-when-calling-afunction功能有关。
1 2 3 4 5 6 7 8 9 10 | >>> 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' ] |
8.列表文字中的星号
Python 3.5 通过PEP 448引入了大量的新功能。最大的新功能之一是能够将可迭代对象转储到新列表中。
假设您有一个函数,该函数可以接收任何序列,并返回一个列表,其中该序列与该序列的反序连接在一起:
1 2 | def palindromify(sequence): return list (sequence) + list ( reversed (sequence)) |
该函数需要将事物转换为列表几次,以连接列表并返回结果。在Python 3.5中,我们可以改为输入:
1 2 | def palindromify(sequence): return [ * sequence, * reversed (sequence)] |
此代码删除了一些不必要的列表调用,因此我们的代码更加高效和可读。
这是另一个例子:
1 2 | def rotate_first_item(sequence): return [ * sequence[ 1 :], sequence[ 0 ]] |
该函数返回一个新列表,其中给定列表(或其他序列)中的第一项移动到新列表的末尾。
* 运算符的这种使用是将不同类型的可迭代对象连接在一起的好方法。的*操作者适用于任何可迭代,而使用+操作者仅适用于具有所有相同类型的特定序列。
这不仅限于创建列表。我们还可以将可迭代项转储到新的元组或集合中:
1 2 3 4 5 6 | >>> 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' } |
请注意,上面的最后一行获取一个列表和一个生成器,并将它们转储到新集中。在使用之前*,以前没有一种简单的方法可以在一行代码中做到这一点。以前有一种方法可以做到,但要记住或发现它并不容易:
1 2 | >>> set ().union(fruits, uppercase_fruits) { 'lemon' , 'watermelon' , 'TOMATO' , 'LEMON' , 'PEAR' , 'WATERMELON' , 'tomato' , 'pear' } |
9.字典文字中的双星号
PEP 448还**允许该运算符用于将键/值对从一个字典转储到新字典中,从而扩展了功能:
1 2 3 4 5 | >>> 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' } |
例如,我们可以在添加新值的同时复制字典:
1 2 3 4 | >>> 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' } |
或在覆盖特定值的同时复制/合并字典:
1 2 3 4 | >>> 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' } |