最近接触了一些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))
以上。