注: 本文是正在编写的一本书的书稿选登。
星号( *
)已经在此前的学习中出现过,它可以作为乘法和乘方的运算符,也可以表示序列中元素的重复。对于函数而言,它的作用则体现在收集参数上。
如果函数的参数个数是确定的,就用7.1节中的方式定义函数,但这个假设并不总成立。例如写一个计算人体一天所摄入能量的函数,参数为这一天所吃的东西,显然每一天所吃的食物的种类数并不都一样,即不能确定要提供多少个参数。这种情况下,就要“收集参数”。
1. 收集位置参数
定义函数时,参数前用一个星号( *
)表示收集位置参数。
>>> def computer_language(*lang):
... print(lang)
...
>>> computer_language('python', 'java', 'rust', 'php')
('python', 'java', 'rust', 'php')
>>> computer_language('pascal', 'python')
('pascal', 'python')
函数 computer_language()
的参数 lang
前面有一个星号,当调用此函数时,可以输入任意多个位置参数——实参,这些参数都被收集到一个元组中,并被变量 lang
引用。
还可以这样:
>>> def computer_language(lang, *others):
... print(f"lang={lang}")
... print(f"others = {others}")
...
>>> computer_language('python', 'php', 'c#') # (1)
lang=python
others = ('php', 'c#')
函数 computer_language()
的形参由两部分组成,lang
同之前的参数含义,*others
则表示用 others
收集其余的实参。从注释(1)的调用中可知,lang
对应第一个对象 'python'
,其余对象则被收集到元组中,并被 others
引用。
下面编写一个函数,用它来挑选诸多数字中的质数(第6章6.6节曾编写了一个关于质数的程序,此处则使用另外一种判断质数的方法)。“诸多”表明个数不确定——当然,可以放到序列中循环,一个一个地判断。
#coding:utf-8
'''
filename: choiceprime.py
'''
import math
def is_prime(n):
if n <= 1:
return False
for i in range(2, int(math.sqrt(n))+1):
if n % i == 0:
return False
return True
def choice(*args):
return [i for i in args if is_prime(i)]
if __name__ == "__main__":
prime_number = choice(1,3,5,7,9,11,13,15,17,19,21,23)
print(prime_number)
函数 is_prime()
用于判断一个自然数是否为质数,参数的个数很明确(此函数的数学原理,请读者自行解决,此处不赘述)。函数 choice()
用于从若干个自然数中选择质数,备选的自然数的个数不确定,故使用 *args
收集参数。
执行程序,结果如下:
% python choiceprime.py
[3, 5, 7, 11, 13, 17, 19, 23]
2. 收集关键词参数
对于关键词参数,可以使用两个星号 **kwargs
的形式收集。
>>> def foo(**kwargs):
... print(kwargs)
...
>>> foo(name='laoqi', age=30)
{'name': 'laoqi', 'age': 30}
>>> foo(a=1, b=2, c=3)
{'a': 1, 'b': 2, 'c': 3}
对于函数 foo()
,不论传入多少个关键词参数,均能收集为一个字典类对象——关键词参数创建了变量与对象的对应关系,并用两个星号后面的变量引用。
如果定义这样一个函数:
>>> def bar(*args, **kwargs):
... print(f"args = {args}")
... print(f"kwargs = {kwargs}")
...
>>> bar(1, 2, 3, lang="python", author="laoqi")
args = (1, 2, 3)
kwargs = {'lang': 'python', 'author': 'laoqi'}
是不是囊括一切,很“万能”了。的确能够收集任何多个位置参数和关键词参数,但是不要认为这种方式推广到任何函数中都是好事。在很多函数中,我们能够明确知道参数的个数,就不需要这种“万能”方式,毕竟在函数体内如果要用到那些对象,还必须给元组或字典“解包”。
用一个星号或者两个星号收集参数,并不一定非要提供数量大于等于
的实参,也可以这样做:
>>> bar(1, 2, 3)
args = (1, 2, 3)
kwargs = {}
>>> bar()
args = ()
kwargs = {}
不会报错,只是元组或字典为空罢了。
特别注意,下面的定义方式是错误的:
>>> def bar(**kwargs, *args):
File "<stdin>", line 1
def bar(**kwargs, *args):
^
SyntaxError: invalid syntax
7.1.3节提到过,位置参数必须在关键词参数之前,收集参数的写法也遵循这个原则。
所谓解包,就是获得容器类对象中的成员。星号用于对容器的解包,其方法与7.2.1的收集参数类似。
>>> lst = [1, 2, 3, 4, 5]
>>> *a = lst # (2)
File "<stdin>", line 1
SyntaxError: starred assignment target must be in a list or tuple
注释(2)的写法不正确,请注意如何修改:
>>> *a, = lst
>>> a
[1, 2, 3, 4, 5]
虽然这么写没有报错,但根本没有实现“解包”的目的。“天生我材必有用”,它可以用在这里:
>>> range(10)
range(0, 10)
>>> *r, = range(10) #(3)
>>> r
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Python 内置函数 range()
返回的是可迭代的 range 对象,第6章6.3.1节曾用 list()
函数对其进行类型转换,才能读取到其成员,这里使用注释(3)即可得到 range 对象中的成员,实现“解包”功能。
根据列表的知识,如果要截取列表中的部分项,可以通过切片操作实现,此外,还可以用下面的方式完成:
>>> a, *b, c = lst # (4)
>>> a
1
>>> b
[2, 3, 4]
>>> c
5
注释(4)分别用变量 a
和 c
引用了列表中第一项和最后一项,其余成员用 b
引用。
以此前写过的加法函数为例,会看到更精彩的解包操作:
>>> def add(x, y):
... return x + y
...
>>> num = [2, 3]
>>> add(*num) # (5)
5
函数 add()
用以实现两个对象的 +
运算,列表 num
中有两个整数,如果让它们两个相加,一种解决方案是通过索引分别得到这两个数,即 add(num[0], num[1])
。现在用星号对这个容器解包,以注释(5)中的 *num
作为函数的参数,即可将其中的两个成员从序列中提取出来,作为函数 add()
的位置参数。
还可以 add()
中的形参名称为键,创建一个字典,然后以下述代码中注释(6)的形式调用函数,从字典中解包出键值对,实现以关键词参数形式向函数传值。
>>> d = {"x": 2, "y": 3}
>>> add(**d) # (6)
5
在第4章4.2.7节学过字符串的一个方法 format()
,用于字符串格式化输出,其参数也可以用两个星号对字典解包(如下述代码注释(7)所示)。
>>> painter = {'name': 'Dynami', 'city': "Soochow"}
>>> "{name} is from {city}".format(**painter) # (7)
'Dynami is from Soochow'
此外,在容器的合并上,也能使用星号,让代码显得更简洁紧凑。
>>> lst1 = [1, 2, 3]
>>> lst2 = [4, 5, 6]
>>> new_lst = [*lst1, *lst2] # (8)
>>> new_lst
[1, 2, 3, 4, 5, 6]
将列表 lst1
和 list2
合并为同一个列表,可以使用 +
将两个列表链接起来,也可以使用注释(8)实现同样的操作。其他类型的容器,也类似:
>>> t1 = (1, 2, 3)
>>> t2 = (3, 4, 5)
>>> (*t1, * t2)
(1, 2, 3, 3, 4, 5)
>>> s1 = {1,2,3}
>>> s2 = {3,4,5}
>>> {*s1, *s2}
{1, 2, 3, 4, 5}
对于字典,当然要用两个星号:
>>> d1 = {'author': 'laoqi', 'lang':'python'}
>>> d2 = {'price': 66, 'age': 30}
>>> d = {**d1, **d2}
>>> d
{'author': 'laoqi', 'lang': 'python', 'price': 66, 'age': 30}
★自学建议 适时总结,是一种非常重要的自学方法。以本节所学习的“星号”为例,从乘法到解包操作,本书中都零零散散介绍过了。读者在学习过程中,如果觉得内容有点凌乱,很希望有人能将所有的东西总结到一张纸上的话。真正的自学者,就不要期望别人做这件事,要自己动手,才能构建起完整的、清晰的、能更新的知识结构。 ”
点击【阅读原文】,查看书稿的有关介绍和更多内容。
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有