如何在 Python 中使用解包运算符 (*, **)?
已发表: 2021-07-02Python 是最常用的编程语言。 今天,您将学习使用其核心功能之一(但经常被忽略),即在 Python 中解包。
您可能已经在其他人的代码中看到 * 和 ** ,甚至在不知道它们的用途的情况下使用过它们。 我们将介绍解包的概念,以及如何使用它来编写更多 Pythonic 代码。
以下是您在阅读本教程时会发现有用的概念列表:
- Iterable :任何可以被for 循环迭代的序列,如集合、列表、元组和字典
- Callable :可以使用双括号()调用的Python对象,例如myfunction()
- Shell:交互式运行时环境,可让我们运行 Python 代码。 我们可以通过在终端中运行“python”来调用它
- 变量:存储对象并具有保留内存位置的符号名称。
让我们从最常见的混淆开始:Python 中的 Asteristics 也是算术运算符。 一个星号 (*) 用于乘法,而其中两个 (**) 表示求幂。
我们可以通过打开 Python shell 并键入以下内容来证明:
>>> 3*3 9 >>> 3**3 27注意:您需要安装 Python 3 才能学习本教程。 如果您没有安装它,请查看我们的 Python 安装指南。
如您所见,我们在第一个数字之后和第二个数字之前使用星号。 当您看到这一点时,这意味着我们正在使用算术运算符。
另一方面,我们在可迭代对象之前使用星号 (*, **) 来解包它——例如:
>>> *range(1, 6), (1, 2, 3, 4, 5) >>> {**{'vanilla':3, 'chocolate':2}, 'strawberry':2} {'vanilla': 3, 'chocolate': 2, 'strawberry': 2}如果你不明白也不要担心,这只是在 Python 中解包的序言。 所以继续阅读整个教程!
什么是开箱?
解包是取出东西的过程——诸如列表、元组和字典之类的可迭代对象。 把它想象成打开一个盒子并取出不同的物品,如电缆、耳机或 USB。

让我们将这个相同的例子翻译成代码以便更好地理解:
>>> mybox = ['cables', 'headphones', 'USB'] >>> item1, item2, item3 = mybox如您所见,我们将mybox列表中的三个项目分配给三个变量item1、item2、item2 。 这种变量赋值是 Python 中解包的基本概念。
如果您尝试获取每个项目的值,您会注意到item1指的是“电缆”, item2指的是“耳机”等等。
>>> item1 'cables' >>> item2 'headphones' >>> item3 'USB'到这里为止,这段代码似乎一切都很好,但是如果我们想解压缩一个包含更多元素的列表 - 保持相同数量的分配变量怎么办?
>>> newbox = ['cables', 'headphones', 'USB', 'mouse'] >>> item1, item2, item3 = newbox Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: too many values to unpack (expected 3)您可能已经预料到了这种错误。 本质上,我们将 4 个列表项分配给三个变量,Python 如何分配正确的值?
它没有,那是因为我们得到了一个 ValueError 和消息“太多的值无法解包”。 这是因为我们在左边设置了三个变量,在右边设置了四个值(对应于 newbox 列表)。
如果您尝试执行类似的过程,但要解压的变量多于值,您将收到另一个ValueError,但消息略有不同:
>>> lastbox = ['cables', 'headphones'] >>> item1, item2, item3 = lastbox Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: not enough values to unpack (expected 3, got 2)注意:我们一直在使用列表,但您可以将这种解包形式用于任何可迭代对象(列表、集合、元组、字典)
那么我们如何克服这种情况呢? 有没有办法将一个可迭代对象的所有项目解包到几个变量中而不会出现任何错误?
当然有,它被称为解包运算符或星号运算符 (*, **)。 让我们看看如何在 Python 中使用它。
如何使用 * 运算符解压列表
星号运算符 (*) 用于解包尚未分配的可迭代对象的所有值。
假设您想在不使用索引的情况下获取列表的第一个和最后一个元素,我们可以使用星号运算符来实现:
>>> first, *unused, last = [1, 2, 3, 5, 7] >>> first 1 >>> last 7 >>> unused [2, 3, 5]如您所见,我们使用星号运算符获取所有未使用的值。 丢弃值的首选方法是使用下划线变量 (_),该变量有时用作“虚拟变量”。
>>> first, *_, last = [1, 2, 3, 5, 7] >>> _ [2, 3, 5]即使列表只有两个元素,我们仍然可以使用这个技巧:
>>> first, *_, last = [1, 2] >>> first 1 >>> last 2 >>> _ []在这种情况下,下划线变量(虚拟变量)存储一个空列表,以便它们周围的其他两个变量可以访问列表的可用值。
常见故障排除
我们可以解压可迭代对象的唯一元素。 例如,你会想出这样的东西:
>>> *string = 'PythonIsTheBest'但是,上面的代码将返回一个SyntaxError :
>>> *string = 'PythonIsTheBest' File "<stdin>", line 1 SyntaxError: starred assignment target must be in a list or tuple这是因为根据 PEP 规范:
简单赋值左侧的元组(或列表)
如果我们想将一个可迭代对象的所有值解包为一个变量,我们必须设置一个元组,因此添加一个简单的逗号就足够了:
>>> *string, = 'PythonIsTheBest' >>> string ['P', 'y', 't', 'h', 'o', 'n', 'I', 's', 'T', 'h', 'e', 'B', 'e', 's', 't']另一个例子是使用range函数,它返回一个数字序列。

>>> *numbers, = range(5) >>> numbers [0, 1, 2, 3, 4]现在您知道如何使用星号解包列表和元组,是时候开始解包字典了。
如何使用 ** 运算符解压字典
虽然单个星号用于解包列表和元组,但双星号 (**) 用于解包字典。
不幸的是,我们无法像处理元组和列表那样将字典解包为单个变量。 这意味着以下将引发错误:
>>> **greetings, = {'hello': 'HELLO', 'bye':'BYE'} ... SyntaxError: invalid syntax但是,我们可以在可调用对象和其他字典中使用 ** 运算符。 例如,如果我们想创建一个由其他字典组成的合并字典,我们可以使用以下代码:
>>> food = {'fish':3, 'meat':5, 'pasta':9} >>> colors = {'red': 'intensity', 'yellow':'happiness'} >>> merged_dict = {**food, **colors} >>> merged_dict {'fish': 3, 'meat': 5, 'pasta': 9, 'red': 'intensity', 'yellow': 'happiness'}这是创建复合字典的一种非常简短的方法,但是,这不是 Python 中解包的主要方法。
让我们看看我们如何使用可调用的解包
在函数中打包:args 和 kwargs
在类或函数上实现之前,您可能已经见过 args 和 kwargs。 让我们看看为什么我们需要将它们与可调用对象一起使用。
使用 * 运算符 (args) 打包
假设我们有一个计算两个数乘积的函数。
>>> def product(n1, n2): ... return n1 * n2 ... >>> numbers = [12, 1] >>> product(*numbers) 12如您所见,我们正在将列表编号解包到函数中,因此我们实际上正在运行以下代码:
>>> product(12, 1) 12到这里为止,一切正常,但是如果我们想传递更长的列表怎么办? 它肯定会引发错误,因为该函数接收到的参数超出了它的管理能力。
>>> numbers = [12, 1, 3, 4] >>> product(*numbers) ... TypeError: product() takes 2 positional arguments but 4 were given我们可以通过直接在函数上打包列表来解决所有这些问题,这会在其中创建一个可迭代对象并允许我们将任意数量的参数传递给函数。
>>> def product(*args): ... result = 1 ... for i in args: ... result *= i ... return result ... >>> product(*numbers) 144在这里,我们将args参数视为可迭代的,遍历其元素并返回所有数字的乘积。 请注意结果的起始编号必须是 1,因为如果我们从零开始,该函数将始终返回零。
注意:args 只是一个约定,您可以使用任何其他参数名称
我们也可以在不使用列表的情况下将任意数字传递给函数,就像内置的打印函数一样。
>>> product(5, 5, 5) 125 >>> print(5, 5, 5) 5 5 5最后,让我们获取函数参数的对象类型。
>>> def test_type(*args): ... print(type(args)) ... print(args) ... >>> test_type(1, 2, 4, 'a string') <class 'tuple'> (1, 2, 4, 'a string')如上代码所述, args的类型将始终是tuple ,其内容将是传递给函数的所有非关键字参数。
使用 ** 运算符打包(kwargs)
正如我们之前看到的,** 运算符专门用于字典。 这意味着使用此运算符,我们可以将键值对作为参数传递给函数。
让我们创建一个函数make_person ,它接收一个位置参数“名称”,以及未定义数量的关键字参数。
>>> def make_person(name, **kwargs): ... result = name + ': ' ... for key, value in kwargs.items(): ... result += f'{key} = {value}, ' ... return result ... >>> make_person('Melissa', id=12112, location='london', net_worth=12000) 'Melissa: id = 12112, location = london, net_worth = 12000, '如您所见, **kwargs语句将所有关键字参数转换为字典,我们可以在函数内部对其进行迭代。
注意:kwargs 只是一个约定,你可以随意命名这个参数
我们可以像使用args一样检查kwargs的类型:
>>> def test_kwargs(**kwargs): ... print(type(kwargs)) ... print(kwargs) ... >>> test_kwargs(random=12, parameters=21) <class 'dict'> {'random': 12, 'parameters': 21}kwargs内部变量总是变成一个字典,它存储传递给函数的键值对。
最后,让我们在同一个函数中使用args和kwargs :
>>> def my_final_function(*args, **kwargs): ... print('Type args: ', type(args)) ... print('args: ', args) ... print('Type kwargs: ', type(kwargs)) ... print('kwargs: ', kwargs) ... >>> my_final_function('Python', 'The', 'Best', language='Python', users='A lot') Type args: <class 'tuple'> args: ('Python', 'The', 'Best') Type kwargs: <class 'dict'> kwargs: {'language': 'Python', 'users': 'A lot'}结论
解包运算符在日常任务中非常有用,现在您知道如何在单个语句和函数参数中使用它们。
在本教程中,您学到了:
- 您将 * 用于元组和列表,将 ** 用于字典
- 您可以在函数和类构造函数中使用解包运算符
- args用于将非关键字参数传递给函数
- kwargs用于将关键字参数传递给函数。
