跳至主要内容

Python 中星号的用法

许多 Python 用户熟悉将星号用于乘法和幂运算符,但在本教程中,您将了解如何以更多方式应用星号。
· 9分钟 阅读

使用 AI 探索

我们大多数人使用星号作为乘法和幂运算符,但在 Python 中,它们作为前缀运算符还有更广泛的用法。读完本文,您将了解星号的完整用法。

星号在 Python 中有许多特定的使用场景。通常,我们熟悉的是乘法和幂运算符。在不同情境下,它还可以执行解包、参数传递等操作。首先,让我们看看 星号 的常规用法。

* 和 ** 的常规用法

* 用作乘法运算符,而 ** 用作幂运算符。

## declaring variables
a, b = 2,3

## multiplication
print("Multiplication of 2 and 3 is {}".format(a * b))

## as power operator
print("2 power 3 is {}".format(a ** b))
Multiplication of 2 and 3 is 6
2 power 3 is 8

现在您已经看到了最常见的星号用法。不过,星号还有一些您可能不熟悉的用法。让我们继续挖掘星号的隐藏用法。

1. 使用 * 进行解包

使用 * 对列表/元组/集合中的元素进行解包。我们将在后续代码中利用解包来实现 星号 的其他特性。解包是将星号作为前缀运算符使用的基本思想。

假设我们想把列表/元组/集合的第一个元素赋给a,其余元素赋给b。一般情况下,我们会使用切片来完成。

## general approach
nums = [i for i in range(1, 6)]
a = nums[0]
b = nums[1:]
print(a, b)

print("--------------------------")

## hard approach
squares = [i ** 2 for i in range(1, 6)]
a = squares[0]
b = []
for i in range(1, len(squares)):
    b.append(squares[i])
print(a, b)
1 [2, 3, 4, 5]
--------------------------
1 [4, 9, 16, 25]

现在,我们将使用 * 运算符用一行代码来实现这个操作。

nums = [i for i in range(1, 6)]

## a will be 1 and b will be a list containing remaining elements
a, *b = nums
print(a, b)
1 [2, 3, 4, 5]

这里的a是从列表中获取到的一个变量,而b是一个列表。解包在元组和集合上与在列表上一样适用。让我们看一些示例。

## using tuple
## here first element will assign to a and last element to c
a, *b, c = (1, 2, 3, 4, 5)
print(a, b, c)

print("--------------------------")

## using sets
a, *b, c = {1, 4, 9, 16, 25}
print(a, b, c)
1 [2, 3, 4] 5
--------------------------
1 [4, 9, 16] 25

1.1. 组合不同的可迭代对象

我们可以使用解包将两个不同的可迭代对象合并到列表/集合/元组中。一般来说,我们需要先转换这些可迭代对象再合并。但如果使用 *,就没有这个必要。来看一个示例。

## combining a tuple, list, set
nums = [1, 2, 3]
nums2 = (4, 5, 6)
nums3 = {7, 8, 9}

## we convert the combined elements into any iterable we want
## here i am converting into a list
_list = [*nums, *nums2, *nums3]
_tuple = (*nums, *nums2, *nums3)
_set = {*nums, *nums2, *nums3}

print(type(_list))
print(_list)
print("------------------------")
print(type(_tuple))
print(_tuple)
print("------------------------")
print(type(_set))
print(_set)
<class 'list'>
[1, 2, 3, 4, 5, 6, 8, 9, 7]
------------------------
<class 'tuple'>
(1, 2, 3, 4, 5, 6, 8, 9, 7)
------------------------
<class 'set'>
{1, 2, 3, 4, 5, 6, 7, 8, 9}

1.2. 嵌套解包

嵌套解包是从已解出的第一层元素中继续取出内部元素。请看示例以便理解我的意思。

languages = ["Python", "HTML", "CSS", "JS"]

## unpacking
[[first_letter, *remaining], *other] = languages

print(first_letter, remaining, other)
P ['y', 't', 'h', 'o', 'n'] ['HTML', 'CSS', 'JS']

2. 使用 ** 进行解包

如前所述,** 用于从字典中解包元素。我们必须知道字典的键,才能在解包时提取对应的值。来看示例。

## sample dictionary
person = {"name":"John", "age":19, "year_of_passing":"2021"}
string = "Name:-{name} Year Of Graduation:-{year_of_passing} Age:-{age}".format(**person)
print(string)
Name:-John Year Of Graduation:-2021 Age:-19

使用 ** 从字典中提取元素并不常见。

3. 函数中的星号

我们可以在函数中使用 星号来传递和接收参数,从而减少代码行数。

3.1. 函数中的解包

* 运算符可用于通过解包可迭代对象来调用函数。假设我们有一个列表,需要将其中所有元素分别传给 print()。您会怎么做?我想您会想到用循环。但 * 让这变得非常简单。

只需以 *iterable_name 的形式传入该可迭代对象。它会解包并将所有元素分别传入。

nums = [i for i in range(1, 6)]
## passsing list using the *
print(*nums)
1 2 3 4 5
nums = (i for i in range(1, 6))
## passsing tuple using the *
print(*nums, sep = ", ")
1, 2, 3, 4, 5

3.2. 打包元素

打包是指一次性接收多个元素。我们在函数调用中会用到这一特性。

def average(*nums):
    return sum(nums) / len(nums)

现在,我们可以传入任意数量的整数来计算平均值。函数 average 会将所有参数接收为一个列表。

## calling the average with some numbers
print(average(1, 2, 3, 4, 5))
3.0

我们已经看到如何使用 * 将多个元素接收为列表。现在,我们来学习如何使用 ** 接收多个键值对元素。

def _object(name, **properties):
    print(name, properties)

_object("Car", color="Red", cost=999999, company="Ferrari")
Car {'ceo': 'Louis', 'color': 'Red', 'cost': 999999, 'company': 'Ferrari'}

4. 仅限关键字参数与位置参数共用

带名称的参数称为关键字参数。其他参数称为位置参数。首先,您需要理解关键字参数与位置参数的区别。让我们看一个示例。

## name is a positional argument
## color and cost are keyword arguments
def sample(car, color = None, cost = None):
    print("Car:-{} Color:-{}, Cost:-{}".format(car, color, cost))

在调用函数时,对于关键字参数,我们可以用参数名来传参。如果我们不给关键字参数传值,它们会采用函数定义中提供的默认值。

我们可以通过参数的关键字或位置来指定关键字参数。

sample("Ferrari", "Red", 999999)
Car:-Ferrari Color:-Red, Cost:-999999
sample("Ferrari", cost = 999999, color = "Red")
Car:-Ferrari Color:-Red, Cost:-999999

如果使用函数中定义的名称来传入关键字参数,那么它们的顺序并不重要。

sample("Ferrari", color = "Green")
Car:-Ferrari Color:-Green, Cost:-None

现在,您已经了解了位置参数与关键字参数。让我们进入主题。

仅限关键字参数只能通过关键字来指定。我们不能像先前那样按位置设置它们。要捕获仅限关键字参数,我们必须在 * 参数之后声明这些关键字参数。

def keyword_only(*items, _list, default = False):
    print(items)
    print(_list)
    print(default)

由于 _list 和 default 出现在 *items 之后,调用函数时必须使用它们的关键字。如果不使用关键字来传入,将会报错。我们来看两种情况。

nums = [i ** 2 for i in range(1, 6)]
## calling the function
keyword_only(1, 2, 3, 4, 5, _list = nums, default = True)
(1, 2, 3, 4, 5)
[1, 4, 9, 16, 25]
True

如果我们用位置参数来调用 keyword_only 函数会怎样?

nums = [i ** 2 for i in range(1, 6)]
## calling the function will raise an error
keyword_only(1, 2, 3, 4, 5, nums, False)
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

<ipython-input-85-bf737d8a8bfc> in <module>()
      1 nums = [i ** 2 for i in range(1, 6)]
----> 2 keyword_only(1, 2, 3, 4, 5, nums, False)


TypeError: keyword_only() missing 1 required keyword-only argument: '_list'

我们得到的错误表明缺少一个必需的仅限关键字参数。因此,在调用仅限关键字参数时,必须使用关键字。仅限关键字参数只在函数中位于 * 参数之后时才得以定义。

5. 无位置参数的仅限关键字参数

如果我们只想接收仅限关键字参数而不接收任何位置参数,Python 允许我们在函数参数中使用 * 来实现。来看一个示例。

def _sample(*, name):
    print(name)

上述函数只接收仅限关键字参数。我们必须使用关键字来传入参数,否则会报错。函数定义中的 * 用来确保函数只能通过关键字来接收参数。在函数中我们在 * 之后定义的任何参数,都必须在调用时以关键字形式指定。

## calling the function using keyword 'name'
_sample(name = "Datacamp")
Datacamp
## calling the function without using keyword 'name'
## we will get a TypeError
_sample("Datacamp")
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

<ipython-input-12-fe0d3718b56c> in <module>()
      1 ## calling the function without using keyword 'name'
      2 ## we will get a TypeError
----> 3 samp("Datacamp")


TypeError: samp() takes 0 positional arguments but 1 was given

很明显,_sample() 函数只接收仅限关键字*参数。

总结

在 Python 的每个新版本中,开发者都会加入新特性。星号很特别,能减少部分代码。如果没有 *,我们无法接收多个位置参数;而要实现仅限关键字参数,也需要使用 *。星号的其他操作也许可以用多行代码实现。多加练习以彻底掌握它们。感谢您的阅读!

如果您想进一步学习 Python,可以参加 DataCamp 的 Python Data Science Toolbox(第 1 部分) 课程。

祝您编码愉快 :)

主题