1.python中的拆包
之前就只写了*可以是未知数量的参数,**可以传入未知数量命名参数。这次详细记下拆包。
def f1(a, *l): print(a) # 不拆包 print(l) # 拆包 print(*l) f2(l) f2(*l) def f2(*l): print(l) print(*l) def f3(**kw): # 不拆包 print(kw) # 得到键名 print(*kw) # 拆包,键名对应 f4(**kw) # 注意,python中没办法输出**kw,不信可以自己试试。。 def f4(a, b): print(a, b) f3(a=1, b=2) f1(1, 2, 3) ''' {'a': 1, 'b': 2} a b 1 2 1 (2, 3) 2 3 ((2, 3),) (2, 3) (2, 3) 2 3 '''
我试图通过这段代码的运行情况搞清楚拆包,实际上结果也特别明显。
其实python给f1和f2函数传入的参数是个元组,也就是形式参数被转换为元组传入了f1和f2中作为实参,这应该是函数给我们做的装包工作,这个工作对我们在函数里操作是很方便的,而从未拆包和拆包的f2的输出来看,我们的理解是正确的。
同样的,**kw传入的参数其实是个字典,传入之后会隐式地进行装包工作,也就是将a=1,b=2装包成{'a':1,'b':2}的字典,这样方便了我们在函数中提取相应参数,我觉得还是很有用的。需要注意的是,我们无法通过print(**kw)得到a=1,b=2的输出结果,因为print函数貌似不支持这种输出,可以自己尝试下。而从f4的输出看出,我们的理解是正确的。
因此,我们可以在以下场景用到拆包操作。
1.我们有一个元组,想把这个元组的数据分别作为参数传入。其实就是上面f2函数实现的。
2.我们有一个字典,想把这个字典的数据分别作为参数传入。其实就是上面f4函数实现的。
2.python中的切片
说实在话,切片用到的次数非常多,因为经常操作字符串、list和numpy,所以这个东西用到的次数非常多。
以一个简单的字符串为例
str = "abcdefg" print(str[0:2:1], str[0:2:-1], str[:], str[::-1], str[::-2]) ''' ab abcdefg gfedcba geca '''
切片最常用的是一个左右中括号的操作;通过指定的下标来访问一组序列的元素,其实就切片操作。
中括号中我们可以指定三个参数,其中第一个参数代表要取的序列的起始index,我们选0,就是从最开始选,第二个参数是结束index,但是不包含这个index对应的数据,从以上输出中我们是可以看出来的,第三个参数是步长,步长为1,那么他就从左往右一个一个取;步长为-1,那么他就从右往左一个一个取,步长为负就是反方向取,因此我们可以填-2,那么他就从右往左以两个步长取元素。
其中呢,后面的冒号是可以省略的,也就是可以不填步长,默认就是1,只有一个冒号而不填任何参数就是复制这个序列,而前两个参数空,只把步长设为1其实就是倒置这个序列。
3.python中的名称空间
python中的名称空间我觉得还是要搞清楚的,这样对理解python这门语言还是很有帮助的。
名称空间(namespace):名称空间是名字和对象的映射。也就是可以把一个namespace理解为一个字典,实际上很多当前的Python实现namespace就是用的字典。各个名称空间是独立的,没有任何关系的,所以一个名称空间中不能有重名,但不同的名称空间是可以重名而没有任何影响。
Build-in
....Global
........Enclosed
............Local
可见确实是个层级关系。
我们来分别解释这四个名称空间,
Build-in:python解释器启动产生的字典,用于存放python内置的名字。
Global:执行python文件时,存放文件级定义的名字,就是被import的时候创建,在解释器退出的时候才被删除。
Enclosed:指的是一个函数被另一个函数包含时的命名空间。
Local:执行文件过程中,调用函数时产生的名称空间,用来存放函数内定义的名字,由于生命周期的原因,函数调用结束后该名字会被删除。
Python在查找变量时候按照从内至外的顺序查找变量,可以说成是LEGBD顺序。
作用域(scope):作用域是Python程序(文本)的某一段或某些段,在这些地方,某个命名空间中的名字可以被直接引用。这个作用域就是这个命名空间的作用域。
下面用代码来理解一下作用域。
from copy import copy a = 5 print("***build-in and globals***") # print(type(list(globals().keys()))) globals_init = copy(list(globals().keys())) print(" ".join(globals_init)) # 输出的是build-in的命名空间下的名字key def f1(b=3): c = 4 print("***globals in f1***", *(globals().keys() - globals_init), sep=" ") # 解释下这里,globals().keys()返回的是list,list相减得到的是set,然后对set解包就可以输出了,也可以把set转化为list用.join输出 print("***locals in f1***", *(locals().keys()), sep=" ") def f2(): c = 5 print("***locals in f2***", *(locals().keys()), sep=" ") print("c=", c) f2() print("c=", c) def f3(): nonlocal c c = 6 print("***locals in f3***", *(locals().keys()), sep=" ") print("c=", c) f3() print("c=", c) f1() ''' ***build-in and globals*** __name__ __doc__ __package__ __loader__ __spec__ __annotations__ __builtins__ __file__ __cached__ copy a ***globals in f1*** globals_init f1 ***locals in f1*** b c c= 4 locals in f2 c c= 4 c= 4 locals in f3 c c= 6 '''
咱们从这里可以看出,不同作用域对应的名称是相互独立的,在函数内如果和函数外有相同的变量名称,会默认把近的那个作为要找到的变量,如果要访问函数外的比如a怎么办呢,很简单,函数里面写上global a 即可,否则你的操作就相当于在函数内的local里建立了一个新变量。函数嵌套的时候使用nonlocal去操作被套函数外的local变量,甚至也可以通过global操作全局变量。上面的例子仔细看看应该是理解了。
4.python中的一些高阶函数总结
分别是map、filter、reduce、sorted。其中sorted函数之前已经写过了,但是当时没有用到参数key,这次查了一下,发现key就是自定义排序规则,比如我们让key=abs,那么就会按照绝对值大小排序,当然我们也可以自定义函数,我这里用lambda定义了匿名函数来对x平方大小排序,然后映射回原list。map函数通过建立一个映射,对后面的range通过调用前面的匿名函数形成一个数值映射,然后得到map结果,我们将这个结果转为list输出。filter在map的基础上呢,相当于加上了一层判断,其实filter也会对后面的list生成一个值的映射,但是filter会利用函数作为参数做一层判断,保留满足条件的结果。reduce函数呢,可以允许我们两两调用函数参数,最终只得到一个结果(如例子),这个例子的计算结果其实是1*2*3*4=24也就是也就是先1*2=2,再2*3=6,再6*4=24的结果。;lambda没什么说的了。不是很全,后面会再总结。
from functools import reduce import random a = map(lambda x: x**2, range(1, 5)) print(list(a)) # [1, 4, 9, 16] b = filter(lambda x: x>0, [1,-5,6,0]) print(list(b)) # [1, 6] c = reduce(lambda x,y: x*y,range(1,5)) print(c) # 24 lst = [x * (-1)**x for x in range(1,10)] random.shuffle(lst) print(lst) d = sorted(lst,key = lambda x: x**2,reverse = True) print(d) # [-3, -5, -7, 4, 6, 8, -1, 2, -9] # [-9, 8, -7, 6, -5, 4, -3, 2, -1]