很多关于Python的网课都没有这方面的内容,或者讲的不详细,CSDN上的相关帖子写的也是稀烂,要么就是搬运的排版不好,看都不想看,在这总结一下,虽然我写python也不多,有些场景我也用的很少,但是以后还能回来看一看或者补充补充。
如果想看外国一些好的论坛或者技术网站,可以在必应上选择国际版或者全英文查找就可以看到,比如stackoverflow,geekforgeeks之类的。
在程序编写中有一句话:存在即合理
那么理解语法最好的方法就是在需求中,在遇到实际问题时理解。
>>> a = ('BUPT', 5000, 'TELE COM')
>>> (college, students, major) = a
>>> college
'BUPT'
>>> students
5000
>>> major
'TELE COM'
众所周知,在C中交换两数有三种方法,但是任何情况都正确的只有一种,需要多定义一个变量保存其中一个值。而在Python中可以利用元组来实现交换:
a = 1
b = 2
a, b = b, a
print("a = %d, b = %d" % (a, b))
##########################
a = 2, b = 1
如果你是从其他语言过来学Python,那肯定是觉得:“哦,牛批,还有贼种操作!”
a, b = b, a
这一步其实拆包装包都有。
右侧的b, a
其实是元组(b, a)
,这是装包
左侧的a, b
就是拆包操作,你写成(a, b) = (b, a)
也可以。
比如,在一个元组中我只想要头尾数据:
a = (1, 2, 3, 4)
x, *_, z = a
print("x = %d" % x)
print("_ = " + str(_))
print("z = %d" % z)
####################
x = 1
_ = [2, 3]
z = 4
可以看到,虽然这样子看起来很像是指针,取出头尾,中间部分存在了一个列表中,但是拆包非常灵活,可以试试其他组合都是可行的。
当然可以用tuple将一个列表初始化为元组,但是这是一种技巧
a = [1, 2, 3, 4]
print("data in a:%d %d %d %d" % a)
############################
print("data in a:%d %d %d %d" % a)
TypeError: %d format: a number is required, not list
很显然,%后面跟的一定得是一个元组,但是a是一个列表,这时就要用到拆包把列表拆了,列表的拆包和元组一样。
a = [1, 2, 3, 4]
print("data in a:%d %d %d %d" % (*a,))
############################
data in a:1 2 3 4
**这里还有个小坑:**后面字典的拆包总让人感觉拆包就像宏函数,但其实根本不是,在拆包后再构成元组时,因为只有一个数据在元组中必须加上逗号,否则:
print("data in a:%d %d %d %d" % (*a))
^
SyntaxError: can't use starred expression here
当然既然列表也能拆包,也可以用拆包将列表变为元组:
In [15]: a = [1, 2, 3, 4]
In [16]: b = (*a,)
In [17]: b
Out[17]: (1, 2, 3, 4)
def ret(x, y):
return x*y
z = (10, 5)
print(ret(*z))
#####################
50
#接拆包应用场景1
a = ('BUPT', 5000, 'TELE COM')
college, students, major = a
def ret(x, y):
return y,x
print(ret(10, 20))
#####################
(20, 10)
可以看到ret函数实现了数值交换,虽然没有显式的返回元组,可以看到打印出的是元组,当然也可以显式地返回元组,结果是一样的,这就是装包。
(20, 10)def ret(x, y):
return (y,x)
print(ret(10, 20))
###############
(20, 10)
有时候,一个函数的参数数量不可预知,这时Python装包的作用就体现出来了。
def fun(*arg):
print(type(arg))
print(arg)
fun(1, 2, 3, 4)
######################
<class 'tuple'>
(1, 2, 3, 4)
这里原理没什么好理解的,硬是想理解可以把arg看作像是C里的指针,把参数装入了arg所指的内存,同时arg是一个元组。
还可以这样:
def fun(**kwarg):
print(type(kwarg))
print(kwarg)
fun(name="xiaoming", age=18)
#############################
<class 'dict'>
{'name': 'xiaoming', 'age': 18}
这个看着像二级指针的东西就是向函数传键值对数量不定的字典的方法。注意参数的形式:a=b,也叫做keword arguments,但是和一般的参数不同的是这里a就是键,在字典中是串,b就是a对应的值,在函数中kwargs就可以当作一个字典用。
举个C的例子:
#include
#include
int calc_sum(int*,int);
int main(int argc, char **argv){
int i,a[argc-1];
for(i=1;i<argc;i++)
a[i-1]=atoi(argv[i]);
printf("sum of a is %d\n", calc_sum(a,argc-1));
return 0;
}
int calc_sum(int *A,int k){
int i,sum=0;
for(i=0;i<k;i++)
sum+=A[i];
return sum;
}
//
./main 10 1 2
13
char **argv
:argv是一个二级指针,其实是一个指针数组的名字,数组中保存char*指针,类似python,**kwargs
保存数据的指针,指针中存的是key,索引key得到values。
int *A
:A是一个一级指针,其实在函数参数中一般是一个数组名,其中保存多个数据。
这些*在函数参数和其他地方的差异:
在汉数参数中*就代表这是一个指针,数组,字典云云。
但是在其他语句中单用*,**。效果不一样,在Python中使用**
,相当于就地展开,怎么展开:
In [4]: ret(**a)
lisi is an adult
In [5]: a = {'name':'lisi', 'age':19}
In [6]: def ret(name, age):
...: if age>=18:
...: print(f'{name} is an adult')
...: else:
...: print(f'{name} is a child')
...:
In [7]: ret(**a)
lisi is an adult
一个字典变成了这样:
ret(name='lisi', age=19)
用print函数验证一下:
In [8]: print(**a)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Input In [8], in <cell line: 1>()
----> 1 print(**a)
TypeError: 'name' is an invalid keyword argument for print()
#说明print中的参数是这样的
print(name='lisi', age=19)
#显然这两个不是print的参数,所以报错
那么字典前面只有一个*呢?
得到结果是健:
In [17]: k1, k2 = a
In [18]: k1
Out[18]: 'name'
In [19]: k2
Out[19]: 'age'
In [20]: a[k1]
Out[20]: 'lisi'
In [21]: a[k2]
Out[21]: 18