翻译:Python 开发者 - 一汀, 英文:Trey Hunner

http://blog.jobbole.com/114655/

Python开发者

在 Python 中有很多地方可以看到***。在某些情形下,无论是对于新手程序员,还是从其他很多没有完全相同操作符的编程语言迁移过来的人来说,这两个操作符都可能有点神秘。因此,我想讨论一下这些操作符的本质及其使用方式。

多年以来,***操作符的功能不断增强。在本文中,我将讨论目前这些操作符所有的使用方法,并指出哪些使用方法只能在目前的 Python 版本中应用。因此,如果你学习过 Python 2 中***的使用方法,那么我建议你至少浏览一下本文,因为 Python 3 中添加了许多***的新用途。

如果你是新接触 Python 不久,还不熟悉关键字参数(亦称为命名参数),我建议你首先阅读我有关Python中的关键字参数的文章。

1、不属于我们讨论范围的内容

在本文中, 当我讨论***时,我指的是***

前缀

操作符,而不是

中缀

操作符。

也就是说,我讲述的不是乘法和指数运算:

>>> 2 * 5

10

>>> 2 ** 5

32

2、那么我们在讨论什么内容呢?

我们讨论的是***前缀运算符,即在变量前使用的***运算符。例如:

>>> numbers = [2, 1, 3, 4, 7]

>>> more_numbers = [*numbers, 11, 18]

>>> print(*more_numbers, sep=', ')

2, 1, 3, 4, 7, 11, 18

上述代码中展示了*的两种用法,没有展示**的用法。

这其中包括:

  1. 使用***向函数传递参数

  2. 使用***捕获被传递到函数中的参数

  3. 使用*接受只包含关键字的参数

  4. 使用*在元组解包时捕获项

  5. 使用*将迭代项解压到列表/元组中

  6. 使用**将字典解压到其他字典中

即使你认为自己已经熟悉***的所有使用方法,我还是建议你查看下面的每个代码块,以确保都是你熟悉的内容。在过去的几年里,Python 核心开发人员不断地为这些操作符添加新的功能,对于使用者来说很容易忽略***‘的一些新用法。

3、星号用于将可迭代对象拆分并分别作为函数参数

当调用函数时,*运算符可用于将一个迭代项解压缩到函数调用中的参数中:

>>> 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函数调用,甚至不需要我们知道列表中有多少个参数。

*运算符在这里远不止是语法糖而已。要想用一个特定的迭代器将所有项作为独立的参数传输,若不使用*是不可能做到的,除非列表的长度是固定的。

下面是另一个例子:

def transpose_list(list_of_lists):

return [

list(row)

for row in zip(*list_of_lists)

]

这里我们接受一个二维列表并返回一个“转置”的二维列表。

>>> transpose_list([[1, 4, 7], [2, 5, 8], [3, 6, 9]])

[[1, 2, 3], [4, 5, 6], [7, 8, 9]]

**操作符完成了类似的操作,只不过使用了关键字参数。**运算符允许我们获取键-值对字典,并在函数调用中将其解压为关键字参数。

>>> date_info = {'year': "2020", 'month': "01", 'day': "01"}

>>> filename = "{year}-{month}-{day}.txt".format(**date_info)

>>> filename '2020-01-01.txt' `

根据我的经验,使用**将关键字参数解压缩到函数调用中并不常见。我最常看到它的地方是在实现继承时:对uper()的调用通常包括***

如 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'

不过,在多次使用**时需要特别小心。Python 中的函数不能多次指定相同的关键字参数,因此在每个字典中与**一起使用的键必须能够相互区分,否则会引发异常。

4、星号用于压缩被传递到函数中的参数

在定义函数时,*运算符可用于捕获传递给函数的位置参数。位置参数的数量不受限制,捕获后被存储在一个元组中。

from random import randint

def roll(*dice):

return sum(randint(1, die) for die in dice)

这个函数接受的参数数量不受限制:

>>> roll(20)

18

>>> roll(6, 6)

9

>>> roll(6, 6, 6)

8

Python 的printzip函数接受的位置参数数量不受限制。*的这种参数压缩用法,允许我们创建像printzip一样的函数,接受任意数量的参数。

**运算符也有另外一个功能:我们在定义函数时,可以使用** 捕获传进函数的任何关键字参数到一个字典当中:

def tag(tag_name, **attributes):

attribute_list = [

f'{name}="{value}"'

for name, value in attributes.items()

]

return f"<{tag_name} {' '.join(attribute_list)}>"

** 将捕获我们传入这个函数中的任何关键字参数,并将其放入一个字典中,该字典将引用attributes参数。

>>> tag('a', href="http://treyhunner.com")

'<a href="http://treyhunner.com">'

>>> tag('img', height=20, width=40, src="face.jpg")

'<img height="20" width="40" src="face.jpg">'

5、只有关键字参数的位置参数

在 Python 3 中,我们现在拥有了一种特殊的语法来接受只有关键字的函数参数。只有关键字的参数是

只能

使用关键字语法来指定的函数参数,也就意味着不能按照位置来指定它们。

在定义函数时,为了接受只有关键字的参数,我们可以将命名参数放在*后:

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']

参数dictionarydefault*keys后面,这意味着它们

只能

被指定为关键字参数。如果我们试图按照位置来指定它们,我们会得到一个报错:

>>> 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'

这种行为是通过 PEP 3102 被引入到 Python 中的。

6、没有位置参数关键字的参数

只使用关键字参数的特性很酷,但是如果您希望只使用关键字参数而不捕获无限的位置参数呢?

Python 使用一种有点奇怪的 单独* 语法来实现:

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

这个函数接受一个迭代器参数,可以按照位置或名字来指定此参数(作为第一个参数),以及关键字参数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参数

必须被指定为关键字参数

我通常在获取任意数量的位置参数时只使用关键字参数,但我有时使用这个*强制按照位置指定一个参数。

实际上,Python 的内置sorted函数使用了这种方法。如果你查看sorted的帮助信息,将看到以下信息:

>>> help(sorted)

Help on built-in function sorted in module builtins:

sorted(iterable, /, *, key=None, reverse=False)

Return a new list containing all items from the iterable inascending order.

A custom key function can be supplied to customize the sort order, and the

reverse flag can be set to request the result in descending order.

sorted的官方说明中,有一个单独的*参数。

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 中的 tuple 解包 文章中的示例。在那篇文章中,我将展示如何使用*操作符作为序列切片的替代方法。

通常当我教*的时候,我告诉大家只能在多重赋值语句中使用一个*表达式。实际来说这是不正确的,因为可以在嵌套解包中使用两个*(我在元组解包文章中讨论了嵌套解包):

>>> 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.0 中的 PEP 是 PEP 3132,其篇幅不是很长。

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)]

这段代码避免了一些不必要的列表调用,因此我们的代码更高效,可读性更好。

下面是另一个例子:

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'}

注意,上面的最后一行使用了一个列表和一个生成器,并将它们转储到一个新的集合中。在此之前,并没有一种简单的方法可以在一行代码中完成这项工作。曾经有一种方法可以做到这一点,可是并不容易被记住或发现:

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'}

我还写了另一篇文章:在Python中合并字典的惯用方法。

不过,**操作符不仅仅可以用于合并两个字典。

例如,我们可以在复制一个字典的同时添加一个新值:

>>> 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'}

10、Python 的星号非常强大

Python 的 *** 运算符不仅仅是语法糖。 *** 运算符允许的某些操作可以通过其他方式实现,但是往往更麻烦和更耗费资源。而且 *** 运算符提供的某些特性没有替代方法实现:例如,函数在不使用 * 时就无法接受任意数量的位置参数。

在阅读了*** 运算符的所有特性之后,您可能想知道这些奇怪操作符的名称。不幸的是,它们的名字并不简练。我听说过* 被称为“打包”和“拆包“运算符。我还听说过其被称为“splat”(来自 Ruby 世界),也听说过被简单地称为“star”。

我倾向于称这些操作符为“星”和“双星”或“星星”。这种叫法并不能区分它们和它们的中缀关系(乘法和指数运算),但是通常我们可以从上下文清楚地知道是在讨论前缀运算符还是中缀运算符。

请勿在不理解*** 运算符的前提下记住它们的所有用法!这些操作符有很多用途,记住每种操作符的具体用法并不重要,重要的是了解你何时能够使用这些操作符。我建议使用这篇文章作为一个备忘单或者制作你自己的备忘单来帮助你在 Python 中使用解***

11、喜欢我的教学风格吗?

想了解更多关于 Python 的知识吗?我每周通过实时聊天分享我最喜欢的 Python 资源、回答 Python 问题。

尚学堂推出《13天搞定Python网络爬虫》视频教程,学习成为Python爬虫工程师,薪资杠杠的!

Python中的星号本质及其使用方法详解相关推荐

  1. python中的class怎么用_对python 中class与变量的使用方法详解

    python中的变量定义是很灵活的,很容易搞混淆,特别是对于class的变量的定义,如何定义使用类里的变量是我们维护代码和保证代码稳定性的关键. #!/usr/bin/python #encoding ...

  2. python中class变量_对python 中class与变量的使用方法详解

    python中的变量定义是很灵活的,很容易搞混淆,特别是对于class的变量的定义,如何定义使用类里的变量是我们维护代码和保证代码稳定性的关键. #!/usr/bin/python #encoding ...

  3. 站长在线Python精讲:在Python中匹配字符串的3个方法详解

    欢迎你来到站长在线的站长学堂学习Python知识,本文学习的是<在Python中匹配字符串的3个方法详解>.本知识点主要内容有:使用match()方法进行匹配.使用search()方法进行 ...

  4. 站长在线Python精讲:在Python中格式化字符串的两种方法详解

    欢迎你来到站长在线的站长学堂学习Python知识,本文学习的是<在Python中格式化字符串的两种方法详解>.本知识点主要内容有:使用%操作符格式化字符串和使用format()方法格式化字 ...

  5. python中堆排序_Python实现堆排序的方法详解

    本文实例讲述了Python实现堆排序的方法.分享给大家供大家参考,具体如下: 堆排序作是基本排序方法的一种,类似于合并排序而不像插入排序,它的运行时间为O(nlogn),像插入排序而不像合并排序,它是 ...

  6. python找图是什么模块-python中模块查找的原理与方法详解

    前言 本文主要给大家介绍了关于python模块查找的原理与方式,分享出来供大家参考学习,下面话不多说,来一起看看详细的介绍: 基础概念 module 模块, 一个 py 文件或以其他文件形式存在的可被 ...

  7. python 获取当前日期和时间,Python中获取当前日期和时间的方法详解

    在Python语言里,我们可以通过调用什么模块或者类函数来得到当前的时间或日期呢?本文就将和大家一起来扒一扒这部分内容,一起来看看吧. 当然你可以使用时间模块(time module),该模块提供了各 ...

  8. python布尔型数组_对numpy中布尔型数组的处理方法详解

    布尔数组的操作方式主要有两种,any用于查看数组中是否有True的值,而all则用于查看数组是否全都是True. 如果用于计算的时候,布尔量会被转换成1和0,True转换成1,False转换成0.通过 ...

  9. python中append函数解析_对python中的pop函数和append函数详解

    对python中的pop函数和append函数详解 pop()函数 1.描述 pop() 函数用于移除列表中的一个元素(默认最后一个元素),并且返回该元素的值. 语法 pop()方法语法: list. ...

最新文章

  1. Coing-二叉树(bibary Tree)
  2. 在字节跳动工作是什么样的体验?
  3. BERT各个场景实例代码
  4. linux tomcat 清空war,Linux下tomcat部署war包
  5. 卧底各大程序员圈,才知道什么才是牛逼的程序员!
  6. 图像及其表达与性质(上)
  7. 剖析《最后生还者 第二部》叙事系统的成就与败笔(上)
  8. weex安装环境_WEEX跨平台开发环境搭建
  9. java 如何发提示_消息提醒-如何实现收到待办给QQ发送提醒?
  10. mac mysql密码错误_解决mac 下mysql安装后root用户登录密码错误问题
  11. 一对一直播社交聊天程序: JAVA中的Socket源码概述
  12. flink基本原理入门
  13. c语言程序开发分为四个步骤,c语言程序开发的步骤
  14. C# WPF网络实时监测客户端
  15. 单片机学习(点阵LED及多模块同时实现)
  16. 中标麒麟yum源地址
  17. Nebula 分布式图数据库介绍
  18. element ui图标使用方法
  19. android pc扩展屏幕分辨率,让闲置的iPad / Android平板电脑成为计算机屏幕的扩展显示!...
  20. 在word里面插入对象显示不全怎么办

热门文章

  1. 键桥通讯4000万投资创投布局大数据
  2. 原生Android也能做Web开发了
  3. Java报表工具技巧--如何在Style Report创建用户自定义报表模板
  4. 中国闪存联盟三百大行动成果发布 IBM存储助力企业突破数据临界点
  5. node.js 多个异步过程判断执行是否完成
  6. 小程序的事件处理参数不能取得
  7. awk 系列:awk 怎么读取标准输入(STDIN)
  8. 最快让你上手ReactiveCocoa之进阶篇
  9. 【OpenCV十六新手教程】OpenCV角检测Harris角点检测
  10. 网站架构探索(2)-CDN基本常识 王泽宾