首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >星号的作用

星号的作用

作者头像
老齐
发布2021-10-21 17:02:42
发布2021-10-21 17:02:42
5.4K00
代码可运行
举报
文章被收录于专栏:老齐教室老齐教室
运行总次数:0
代码可运行

7.2 星号的作用

注: 本文是正在编写的一本书的书稿选登。


星号( * )已经在此前的学习中出现过,它可以作为乘法和乘方的运算符,也可以表示序列中元素的重复。对于函数而言,它的作用则体现在收集参数上。

7.2.1 收集参数

如果函数的参数个数是确定的,就用7.1节中的方式定义函数,但这个假设并不总成立。例如写一个计算人体一天所摄入能量的函数,参数为这一天所吃的东西,显然每一天所吃的食物的种类数并不都一样,即不能确定要提供多少个参数。这种情况下,就要“收集参数”。

1. 收集位置参数

定义函数时,参数前用一个星号( * )表示收集位置参数。

代码语言:javascript
代码运行次数:0
运行
复制
>>> 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 引用。

还可以这样:

代码语言:javascript
代码运行次数:0
运行
复制
>>> 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节曾编写了一个关于质数的程序,此处则使用另外一种判断质数的方法)。“诸多”表明个数不确定——当然,可以放到序列中循环,一个一个地判断。

代码语言:javascript
代码运行次数:0
运行
复制
#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 收集参数。

执行程序,结果如下:

代码语言:javascript
代码运行次数:0
运行
复制
% python choiceprime.py 
[3, 5, 7, 11, 13, 17, 19, 23]

2. 收集关键词参数

对于关键词参数,可以使用两个星号 **kwargs 的形式收集。

代码语言:javascript
代码运行次数:0
运行
复制
>>> 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() ,不论传入多少个关键词参数,均能收集为一个字典类对象——关键词参数创建了变量与对象的对应关系,并用两个星号后面的变量引用。

如果定义这样一个函数:

代码语言:javascript
代码运行次数:0
运行
复制
>>> 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'}

是不是囊括一切,很“万能”了。的确能够收集任何多个位置参数和关键词参数,但是不要认为这种方式推广到任何函数中都是好事。在很多函数中,我们能够明确知道参数的个数,就不需要这种“万能”方式,毕竟在函数体内如果要用到那些对象,还必须给元组或字典“解包”。

用一个星号或者两个星号收集参数,并不一定非要提供数量大于等于

1

的实参,也可以这样做:

代码语言:javascript
代码运行次数:0
运行
复制
>>> bar(1, 2, 3)
args = (1, 2, 3)
kwargs = {}
>>> bar()
args = ()
kwargs = {}

不会报错,只是元组或字典为空罢了。

特别注意,下面的定义方式是错误的:

代码语言:javascript
代码运行次数:0
运行
复制
>>> def bar(**kwargs, *args):
  File "<stdin>", line 1
    def bar(**kwargs, *args):
                      ^
SyntaxError: invalid syntax

7.1.3节提到过,位置参数必须在关键词参数之前,收集参数的写法也遵循这个原则。

7.2.2 解包

所谓解包,就是获得容器类对象中的成员。星号用于对容器的解包,其方法与7.2.1的收集参数类似。

代码语言:javascript
代码运行次数:0
运行
复制
>>> 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)的写法不正确,请注意如何修改:

代码语言:javascript
代码运行次数:0
运行
复制
>>> *a, = lst
>>> a
[1, 2, 3, 4, 5]

虽然这么写没有报错,但根本没有实现“解包”的目的。“天生我材必有用”,它可以用在这里:

代码语言:javascript
代码运行次数:0
运行
复制
>>> 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 对象中的成员,实现“解包”功能。

根据列表的知识,如果要截取列表中的部分项,可以通过切片操作实现,此外,还可以用下面的方式完成:

代码语言:javascript
代码运行次数:0
运行
复制
>>> a, *b, c = lst    # (4)
>>> a
1
>>> b
[2, 3, 4]
>>> c
5

注释(4)分别用变量 ac 引用了列表中第一项和最后一项,其余成员用 b 引用。

以此前写过的加法函数为例,会看到更精彩的解包操作:

代码语言:javascript
代码运行次数:0
运行
复制
>>> 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)的形式调用函数,从字典中解包出键值对,实现以关键词参数形式向函数传值。

代码语言:javascript
代码运行次数:0
运行
复制
>>> d = {"x": 2, "y": 3}
>>> add(**d)    # (6)
5

在第4章4.2.7节学过字符串的一个方法 format() ,用于字符串格式化输出,其参数也可以用两个星号对字典解包(如下述代码注释(7)所示)。

代码语言:javascript
代码运行次数:0
运行
复制
>>> painter = {'name': 'Dynami', 'city': "Soochow"}
>>> "{name} is from {city}".format(**painter)    # (7)
'Dynami is from Soochow'

此外,在容器的合并上,也能使用星号,让代码显得更简洁紧凑。

代码语言:javascript
代码运行次数:0
运行
复制
>>> lst1 = [1, 2, 3]
>>> lst2 = [4, 5, 6]
>>> new_lst = [*lst1, *lst2]    # (8)
>>> new_lst
[1, 2, 3, 4, 5, 6]

将列表 lst1list2 合并为同一个列表,可以使用 + 将两个列表链接起来,也可以使用注释(8)实现同样的操作。其他类型的容器,也类似:

代码语言:javascript
代码运行次数:0
运行
复制
>>> 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}

对于字典,当然要用两个星号:

代码语言:javascript
代码运行次数:0
运行
复制
>>> d1 = {'author': 'laoqi', 'lang':'python'}
>>> d2 = {'price': 66, 'age': 30}
>>> d = {**d1, **d2}
>>> d
{'author': 'laoqi', 'lang': 'python', 'price': 66, 'age': 30}

自学建议 适时总结,是一种非常重要的自学方法。以本节所学习的“星号”为例,从乘法到解包操作,本书中都零零散散介绍过了。读者在学习过程中,如果觉得内容有点凌乱,很希望有人能将所有的东西总结到一张纸上的话。真正的自学者,就不要期望别人做这件事,要自己动手,才能构建起完整的、清晰的、能更新的知识结构。 ”

点击【阅读原文】,查看书稿的有关介绍和更多内容。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-10-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 老齐教室 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 7.2 星号的作用
    • 7.2.1 收集参数
    • 7.2.2 解包
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档