万物皆函数
函数是可复用的实现特定功能的代码片段
函数
先定义后执行
语法
1 2 3 4
| def fn(...arg): .... return ... fn(arg)
|
参数与返回值
去重排序
1 2 3 4 5 6 7 8 9 10 11
| def fn(List): """ :param List:传入需要去重排序的列表 :return L:处理后的列表 """ L=[*{*List}] L.sort() return L List=fn([1,1,5,3,3,5,6,22,6]) help(fn) print(List)
|
函数嵌套
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| def fn_a(List1,List2): """ :param List1: 待合并数组1 :param List2: 待合并数组2 :return: 处理后列表 """ return fn_b([*List1,*List2])
def fn_b(List): """ :param List: 待去重列表 :return: 处理后列表 """ return fn_c([*{*List}])
def fn_c(List): """ :param List: 待排序列表 :return: 处理后列表 """ L=List L.sort() return L res=fn_a([1,2,3,4,5,6,7,8,9],[19,33,42,1,3,4]) print(res)
|
在上述代码执行过程中,入栈先后顺序为a,b,c,其中指针指向栈顶,当执行到对应调用函数时,函数入栈;函数结束后函数出栈
函数变量作用域
即变量的作用范围(使用范围)
在函数内定义的变量称为局部变量,无法在函数外部使用,以此保证函数内数据不被污染,该内容可参考前端JS进阶回顾1,在py中函数内无法直接对全局变量做修改,以此我们可以使用global关键词,其效果类似在函数内部导入外部参数、再进行操作
1 2 3 4 5 6
| num=1 def fn(): num=10 print(num) fn() print(num)
|
1 2 3 4 5 6 7 8
| num=1 def fn(): global num num=10 print(num) fn() print(num)
|
参数详解
传参方式
调用函数时,传递实参的方式
位置参数:定义函数时参数的位置进行传参数,调用函数是传入参数顺序与定义函数时的形参一致
关键字参数:调用函数时传入键值对,函数执行时通过读取键值(key=value形式)对中形参作为关键字的值作为实参
(ps:当位置参数与关键字参数混用时,必须先传入位置参数)
默认参数
即在定义函数时,为参数提供默认值,调用函数时可不传递有默认值的参数
1 2 3 4
| def fn(a=1): print(a) fn() fn(10)
|
不定长参数
定义函数时无法预料传入参数数目时,我们使用不定长参数
位置传参-不定长参数,*arg将传入参数封装成元组args
1 2 3 4
| def fn(*args): print(*args) fn(1,2,3,4,5)
|
关键字传参-不定长传参,**kwargs将传入的关键字参数封装成字典
1 2 3 4 5 6 7
| def fn(*args,**kwargs): print(*args) if kwargs.get('a'): print(kwargs['a']) fn(1,2,3,4,5,a=6)
|
参数类型
普通参数:数字、布尔、字符串、数据容器
特殊参数:函数
1 2 3 4
| def fn(x,y,oper): return oper([x,y]) print(fn(1,2,sum))
|
匿名函数
使用lambda定义匿名函数,函数体只能单行表达式
1 2 3 4 5
| """ 语法:lambda 参数列表:函数体 """ fn=lambda x,y:x+y fn(1,2)
|
练习
对列表的元素按照字符长度进行排序
1 2 3 4 5 6 7 8 9 10 11
| def fn(L): """ 可将列表内元素根据字符长度排序 :param L:待排序列表 :return:处理后列表 """ L.sort(key=lambda item:len(item)) return L res=fn(['cs','s','uiwhe','qwh']) print(res)
|
sort()本质也是一个函数,存在key、reverse(bool,是否逆置,默认为false)两个关键字参数,key可传递一个函数指定排序标准,上述代码中指定了len作为标准排序,这也是匿名函数最典型应用。
类型注解
调用函数传参时,实参类型与形参功能实现时发生类型冲突(即不一致)时,我们可以使用类型注解来保证开发中参数传递时类型准确。
1 2 3 4 5 6 7
| num:int=9 str_list:list[str]=list('abc') dict_s_i:dict[str,int]=dict({'abc':1}) print(dict_s_i) print(str_list)
|
py中还具备类型推断机制,是由py解释器自动推断出已赋值的变量、表达式、返回值的数据类型,无需开发者显式声明,因此可以不对已赋值变量做手动的类型注解
函数的类型注解
对函数参数及函数返回值做注解
1 2 3 4 5 6 7 8 9 10
| def fn(l:list[str])->list[str]: """ 可将列表内元素根据字符长度排序 :param L:待排序列表 :return:处理后列表 """ l.sort(key=lambda item:len(item)) return l res=fn(['cs','s','uiwhe','qwh']) print(res)
|
模块
每个py文件都是一个模块,在主文件中导入外部py文件,能够使用外部py文件内定义的函数。
使用模块能够提高代码的复用性、降低开发门槛、避免命名冲突
导入模块
先导入、后使用
1 2 3 4 5
| import 模块名 import 模块名 as 别名 from 模块名 import 功能名 from 模块名 import 功能名 as 别名 from 模块名 import *
|
自定义模块
即将代码分模块开发,再使用导入模块在主模块中导入执行,模块化开发有助于项目的维护管理及复用
在自定义模块中如果对模块内的代码进行测试,存在测试代码,而导入时不希望测试代码运行,则可以使用**name**的内置变量,来表示当前模块名字(直接运行当前模块时,__name__的值为”main“,被导入时为该模块的名称。
可以将测试代码放入判断语句中
1 2 3
| if __name__=='__main__': ...
|
在导入模块中
软件包
上述中的模块能够让项目模块化开发,但大型项目中,模块也会非常多,因此我们可以使用软件包来对多个模块做归类,本质其实是新建文件夹存放,但特殊存在的一个**init.py**文件来描述当前包的信息
导入方式
在不同文件夹下时使用绝对路径进行导入根目录下文件来寻找软件包(根目录文件.包名)
1 2 3 4 5
| import 包名.模块名 from 包名 import 模块名 from 包名 import * from 包名.模块名 import 功能名 from 包名.模块名 import *
|
包内信息
1 2 3 4
| __version__='v1.0.0' __author__='YX' __all__=[...]
|