c++ list用法_*运算符在Python中的解压、传参用法

*运算符在python中的解压、传参用法

这两天得了一本《Python Cookbook》,甚是喜爱,希望能记录一点有意思的Python 用法,既是作为自己的读书笔记,让自己能够不断精进技术,也是为了能够和大家一起讨论Python。

从zip说开去

*操作符在Python中除了乘法和复制(如[1] * 5)的用法之外,还有解压(unpack)的功能,这应该是所有用过zip()的人都知道的事情。zip函数的基本用法如下:

stuff = ['apple','banana','peach']
money = [10, 5, 7]

pair = list(zip(stuff,money))
# pair = [('apple',10),('banana',5),('peach',7)]

但是如果我们现在已经有 pair 这个 list 了,希望能够还原成stuff 和 money 两个 list,我们就需要用到*符号:

stuff,money = zip(*pair)

在这里*就起到了解压的效果

*对于迭代对象的作用

当然了,*并不是说只能与 zip() 联合使用,事实上,任何一个函数都可以用到*符号,只要它操作的对象是可迭代的:

def do_something(x,y,z):
    print('x is ', x)
    print('y is ', y)
    print('z is ', z)

list1 = ['a','b','c']

>>> do_something(list1)
Trackback (most recent call last):
   File "<stdin>", line1, in <module>
TypeError: do() missing 2 required positional arguments: 'y' and 'z'

>>> do_something(*list1)
x is a
y is b
z is c

可知,*操作符自动将 list1 中的元素赋给了三个形参,这就是*很方便的用法

**的用法

当然,对于字典(dict)来说,我们也可以使用*运算符:

dict1 = {'x':1, 'y':2, 'z':3}

>>> do_something(*dict1)
x is x
y is y
z is z

但是这样子得到的是 key 值,如果想要得到 value 值,我们需要使用**运算符:

>>> do_somthing(**dict1)
x is 1
y is 2
z is 3

一定要注意的是,此处的用法必须要求函数形参与字典 key 值一一对应,请看下面的用法:

dict2 = {'z':1, 'x':2, 'y':3}
>>> do_something(**dict2)
x is 2
y is 3
z is 1

dict3 = {'a':1, 'b':2, 'c':3}
>>> do_something(**dict3)
# TypeError: do_something() missing 1 required positional argument: 'x'

实际上,**的用法应该这么理解:

**dict2 等效于 z=1, x=2, y=3, 因此 do_something(**dict2) 等效于 do_something(z=1, x=2, y=3), 这样看来,dict3的错误就是必然的了,因为函数没有 a 这个参数

为了加深理解,请看下面这条例子(来自Reference 2):

>>> date_info = {'year': "2020", 'month': "01", 'day': "01"} 
>>> filename = "{year}-{month}-{day}.txt".format(**date_info) 
>>> filename
'2020-01-01.txt'

理解 *args**kwargs

*args**kwargs是我们经常见到的两个参数,但这两个参数究竟是干什么的可能有些人仍有疑惑。其实它们的用法也很简单,对于一个函数而言,如果你不确定它还有什么待传入的参数,就不妨用一个*args(当然你不一定非得叫args,但一般都喜欢起这个名字),它将输入的多余形参以元组形式保存在args中:

#两个数的加法
def adder_2(x,y):
  return x+y

#三个数的加法
def adder_3(x,y,z):
  return x+y+z

# 无限个数的加法
def adder_unlimited(*args):
   result = 0
   for num in args:
       result += num
   return result

>>> adder_unlimited(1)
1
>>> adder_unlimited(1,2)
3
>>> adder_unlimited(1,2,3,4)
10

>>> list_num = [1,2,3,4]
>>> adder_unlimited(*list_num)  #活学活用
10

**kwargs效果则是将输入的未定义的形参及其对应的值存在kwargs字典里(例子来源Reference 4):

def intro(**data):
    print("nData type of argument:",type(data))
    for key, value in data.items():
        print("{} is {}".format(key,value))

>>> intro(Firstname="Sita", Lastname="Sharma", Age=22, Phone=1234567890)

Data type of argument: <class 'dict'>
Firstname is Sita
Lastname is Sharma
Age is 22
Phone is 1234567890

>>> intro(Firstname="John", Lastname="Wood", Email="johnwood@nomail.com", Country="Wakanda", Age=25, Phone=9876543210)

Data type of argument: <class 'dict'>
Firstname is John
Lastname is Wood
Email is johnwood@nomail.com
Country is Wakanda
Age is 25
Phone is 9876543210

因此,对于一个好的 API 来说,应该尽量使用*args**kwargs以提高程序稳定性

利用*从可迭代对象中分解元素

说了这么多终于说到了《Python Cookbook》中的内容了(内心OS:累死我了)。 假设我们现在要对一个歌手的表现打分,去掉一个最高分,去掉一个最低分,对其他所有分数(未知个数)求平均分,这时*方法就派上用场了:

def drop_first_last(grades):
   first, *middle, last = grades
   return avg(middle)

也许你会说,用切片也可以达到同样效果,如middle = grades[1:-1],但如果我们需要用到firstlast呢?你是不是还要first = grades[0]last = grades[-1]呢? 太不优雅了! 事实上,下面这个例子如果用切片来写就十分麻烦(来源 Reference 3):

>>>line = 'nobody:*:-2:-2:Unprevileged User:/var/empty:/usr/bin/false'
>>> uname,*_, homedir, sh = line.split(':')
>>> uname
'nobody'
>>> homedir
'/var/empty'
>>> sh
'/usr/bin/false'

其中*_将我们 split 之后不想要的部分直接丢弃了(一般用下划线来为我们不需要的变量取名)

实际上,*操作和函数式语言中的列表处理功能相似,相信学过Coursera 华盛顿大学神课Programming Language的同学对这一形式一定不会陌生(来源 Reference3):

def sum(items):
   head, *tail = items
   return head + sum(tail) if tail else head

当然,限于Python自身内部对递归的次数限制,这个例子在实践中意义不大,但是它确实是十分精妙,令人不禁击节赞叹!

其他奇技淫巧

还有一些我不知道怎么分类的小技术,在这里也一并献给大家(例子来自 Reference 2):

列表元素快速换位置

sequence = [*sequence[1:], sequence[0]] #将首个元素换到最后一个

将多个迭代对象转化为 list

很多时候,我们在使用完一个函数时,他返回的值都是generator 等迭代对象,必须套上一个list()才可以变为列表,如zip()reversed()等,此时如果每个函数都套上list()则显得不够优雅,更好的方式如下:

def palindromify(sequence): 
    return list(sequence) + list(reversed(sequence)) #不够优雅

def palindromify(sequence):
    return [*sequence, *reversed(sequence)] # better

同理,多个 list 的拼接也可以如法炮制:

list_all = [*list1,*list2,*list3,*list4]

字典的合并

同理,字典的合并用**也是十分方便:

>>> date_info = {'year': "2020", 'month': "01", 'day': "01"} 
>>> track_info = {'artist': "Beethoven", 'title': 'Symphony No 5'} 
>>> all_info = {**date_info, **track_info} 
>>> all_info {'year': '2020', 'month': '01', 'day': '01', 'artist': 'Beethoven', 'title': 'Symphony No 5'}

最后的闲言碎语

第一次写技术博客,竟然写了三个小时,大概算是涵盖了大部分*的用法,希望这是一个好的开始,也希望所有读者能多提些宝贵意见,谢谢! 之后我还想补充一下Python中*是如何实现的,不过这个可能得去翻源码才能有所斩获吧!

Reference

  1. Python Zip()
  2. Asterisks in Python: what they are and how to use them
  3. Python Cookbook
  4. Python *args and **kwargs
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值