本篇文章将用最通俗易懂的例子,由浅入深地讲解星号的 5 种使用场景。
最简单的用法是利用星号asterisks作为中缀运算符infix operators:
*
用于乘法运算multiplication operation。**
,用于指数运算(也称为幂运算)exponentiation operation。multiplication_operation = 2*3
print(multiplication_operation) # 6
exponentiation_operation = 2**3
print(exponentiation_operation) # 8
如果您需要更多的灵活性flexibility,甚至不确定将传递多少个参数arguments,那么函数并不一定要接收固定数量的参数arguments。下面是星号的表演时间asterisks’ showtime。
def print_genius(*names):
print(type(names))
for n in names:
print(n)
print_genius('Elon Mask', 'Mark Zuckerberg ', 'Zhang San')
# <class 'tuple'>
# Elon Mask
# Mark Zuckerberg
# Zhang San
def top_genius(**names):
print(type(names))
for k, v in names.items():
print(k, v)
top_genius(Top1="Elon Mask", Top2="Mark Zuckerberg", Top3="Zhang San")
# <class 'dict'>
# Top1 Elon Mask
# Top2 Mark Zuckerberg
# Top3 Zhang San
正如上面的例子所示,当定义一个函数function时,我们可以定义前缀为一个或两个星号asterisks的参数parameter来捕获无限数量的参数arguments。
*
为前缀的参数可以将任意多个位置参数positional arguments并入一个 tuple
中。*
为前缀的参数可以将任意数量的关键字参数keyword arguments捕捉到一个 dict
中。按照惯例By convention,如果无法确定函数参数的个数,我们就定义如下函数:
def func(*args, **kwargs):
pass
星号asterisks的一个很酷的用法是让函数function只能接收关键字参数keyword arguments。
一个例子胜过千言万语:
def genius(*, first_name, last_name):
print(first_name, last_name)
# genius('Zhang','San')
# TypeError: genius() takes 0 positional arguments but 2 were given
genius(first_name='Zhang', last_name='San')
# Zhang San
如上例所示,只有一个 *
可以限制以后的所有参数必须以关键字参数keyword arguments进行传递。
实际上,如果我们只想限制一些参数arguments只能使用关键字keyword-only,而保留一些位置参数positional arguments。我们只需将位置参数positional arguments放在星号asterisk之前即可。
def genius(age, *, first_name, last_name):
print(first_name, last_name, 'is', age)
genius(28, first_name='Zhang', last_name='San')
# Zhang San is 28
我们可以使用星号asterisks来对可迭代对象Iterables进行拆包unpack,这将使我们的程序更加清晰和优雅。
例如,如果我们要将不同的可迭代对象Iterables(如一个列表list、一个元组tuple和一个集合set)组合成一个新的列表list,哪种方法最好?
当然,我们可以使用 for-loops 循环遍历所有项目,并将其逐一添加到新列表list中:
A = [1, 2, 3]
B = (4, 5, 6)
C = {7, 8, 9}
L = []
for a in A:
L.append(a)
for b in B:
L.append(b)
for c in C:
L.append(c)
print(L)
# [1, 2, 3, 4, 5, 6, 8, 9, 7]
显然,这种方法可以完成我们的任务,但代码看起来很长,而且不太 “Pythonic”。
更好的方法是使用列表推导式list comprehensions:
A = [1, 2, 3]
B = (4, 5, 6)
C = {7, 8, 9}
L = [a for a in A] + [b for b in B] + [c for c in C]
print(L)
# [1, 2, 3, 4, 5, 6, 8, 9, 7]
我们将三个 for 循环简化为一行列表理解。这已经是 Pythonic 了,但不一定是最简单的!
是时候看看星号有多美了。
A = [1, 2, 3]
B = (4, 5, 6)
C = {7, 8, 9}
L = [*A, *B, *C]
print(L)
# [1, 2, 3, 4, 5, 6, 8, 9, 7]
如上所述,我们可以使用星号asterisk作为可迭代对象iterables的前缀来拆包unpack它们的项目items。
顺便说一下,如果我们使用单个 *
作为 dict
的前缀,其键keys将被拆包unpacked。
如果我们使用双星号 **
作为前缀,那么其值values将被拆包unpacked。但是,我们必须使用它们的键keys来接收拆包unpacked后的值values。由于这种不便,用星号asterisks提取 dict
中的项items并不常见。
D = {'first': 1, 'second': 2, 'third': 3}
# 注意是3个参数
print(*D, sep="...") # first...second...third
# print(**D)
# TypeError: 'first' is an invalid keyword argument for print()
print('{first},{second},{third}'.format(**D)) # 1,2,3
PEP 3132 引入了这种拆包语法,以使我们的代码更加优雅。
这个PEP提出了一种新的语法,允许我们在解包unpacking可迭代对象iterable时,使用一个“catch-all”名称来捕获所有未被分配给其他名称的项目items,并将它们存储在一个列表中。这样就可以更方便地处理不确定数量的项目items。
L = [1, 2, 3, 4, 5, 6, 7, 8]
a, *b = L
print(a)
# 1
print(b)
# [2, 3, 4, 5, 6, 7, 8]
官方翻译:本 PEP 提议修改可迭代解包语法,允许指定一个 “全包catch-all” 名称,该名称将被分配一个未分配给 “常规regular” 名称的所有项目的列表。
一个例子胜过千言万语An example says more than a thousand words:
a, *b, c = range(5)
print(a) # 0
print(b) # [1, 2, 3]
print(c) # 4
星号asterisk是程序中最常用的运算符operators之一。除了用作乘法运算符multiplication operator外,它在 Python 中还有一些优雅而强大elegant and powerful的用法,这将帮助我们的代码变得更加 “Pythonic”。