python *和**以及*_的用法

最近接触了一些python有意思的操作,记录下来

python *和**的解压缩

在python函数传参中,单个*例如*args在函数传参中代表传入不确定个数的参数,而**例如**kwargs代表传入长度不确定的a=1

以上两种用法在一些源码中非常常见,例如在某机器学习库中

此时我们只需对应的传入相应的参数即可。

而在实际的运用中,***可以分别作为列表和字典的解压缩符号使用,例如下面两种使用方式。

>>> print(*[1], *[2], 3)
1 2 3
>>> dict(**{'x': 1}, y=2, **{'z': 3})
{'x': 1, 'y': 2, 'z': 3}

以上几种情况都比较常见,一般百度搜索都能搜到,也是基本常识,下面说一个今天遇到有意思的用法

*_的省略用法

在使用python自带函数os.walk()时,为了获取当前文件夹下的文件和文件夹名称,需要只让os.walk()运行一次。我在网络上查询到以下用法

>>> a, *_ = os.walk(file_dir)
# 此时a返回的结果为一次查询下的当前文件夹下所有文件和文件夹的名称(一层查询)
# 如果改成下列两种形式,均会报错
>>> a, _* = os.walk(file_dir)
>>> a, _ = os.walk(file_dir)

这种现象引起了我的好奇,下面对此做一个探究。

先说结论:说到底*_就是对_的重复,代表多个_,类似于快速生成列表的用法:[1]*3

我们都知道python在接受函数返回值时,若某个返回值今后不需要,可以用_代替,减少变量的使用。则*_就代表有多个返回值不需要,只需要第一个返回值a。

这里我们可以去查阅os.walk()的源码,里面多次出现了python关键字yield,查阅得知,带有yield函数是一个生成器<generator>,简而言之就是你可以把yield当作一个特殊的return,第一次调用带有yield函数时,函数运行到yield会返回,再次调用此函数时,函数会从上次yield的地方继续运行。这里有一个讲的比较好的博客,就不再赘述。
ps:range()也是生成器,所以可以用for...in的形式

这里要讲的是生成器的返回值问题,已知os.walk()是生成器,生成器的一般用法是通过next()函数调用,例如这里一个简单的生成器:

>>> gen = (x * x for x in range(4)) # 这是一个简单生成器的产生方式
>>> print(next(gen))	# 调用返回0*0
0
>>> print(next(gen))	# 再次调用返回1*1
1
>>> print(next(gen))	# 再次调用返回2*2
4

如果我们不用next(),而是使用赋值语句,则应该根据生成器生成的所有返回值的个数进行赋值。

# 例如(x * x for x in range(4))一共可以生成四个数0,1,4,9
>>> a, b = (x * x for x in range(4))	# 两个值接受,报错,返回值不匹配
>>> a, b, c, d = (x * x for x in range(4)) # 成功。a, b, c, d值分别为0, 1, 4, 9

这里就回到我们一开始的问题了,*_的作用,就是省略之后的若干个_来接收返回值,如下面例子

>>> a, *_ = (x * x for x in range(4))	# 结果a为0
>>> *_, a = (x * x for x in range(4))	# 结果a为9

所以,按照上面的说法

a, *_ = (x * x for x in range(4))等价于a = next((x * x for x in range(4))

也就是,回到最开始的os.walk()函数
a, *_ = os.walk(file_dir)等价于a = next(os.walk(file_dir))

以上。

posted @ 2019-10-31 21:30  挂机的阿凯  阅读(2114)  评论(0)    收藏  举报