Python之路(三)——函数
本节内容
- 函数基本语法及特性
- 参数
- 局部变量
- 返回值
- 嵌套函数
- 递归
- 匿名函数
- 高阶函数
- 高级函数
- 内置函数
一、函数基本语法和特性
定义
函数是指将一组语句的集合通过一个名字(函数名)封装起来,要想执行这个函数,只需调用其函数名即可
特性
- 减少重复代码
- 使程序变的可扩展,称作"解耦"
- 使程序变得易维护
基本语法
def cal(x,y): ''' 计算两个数的和 :param x: 第一个数,可以是整数或者浮点数 :param y: 第二个数,可以是整数或者浮点数 :return: x+y的值 ''' return x+y print(cal(2,3)) # 5
二、参数
默认参数
def incre(x, step=1): ''' 返回一个数 + 10次step 的值 :param x: 一个数 :param step: 默认需要累加的基础值 :return: ''' for i in range(10): x += step return x print(incre(5)) # 15 print(incre(5,2)) # 25
非默认参数必须在默认参数之前,否: SyntaxError: non-default argument follows default argument
位置参数与关键字参数传递
users = [] def add_user(u_id,u_name,u_passwd='123'): ''' 新增用户,添加到users 列表中 :param u_id: 用户id :param u_name: 用户名 :param u_passwd: 用户密码 :return: 多用户列表 ''' users.append({ "u_id": u_id, "u_name": u_name, "u_passwd": u_passwd, }) return users #调用程序代码 if __name__ == '__main__': add_user(1,'alex1') # 按照位置传递参数 u_id = 1,u_name='alex1',默认参数u_passwd='123' add_user(2,u_name='alex2',u_passwd='abc')# 按照位置和关键字参数类型混合传递 print(users) # [{'u_id': 1, 'u_name': 'alex1', 'u_passwd': '123'}, {'u_id': 2, 'u_name': 'alex2', 'u_passwd': 'abc'}]
注意:
- 函数定义中'varname=var'形式为默认参数,区别于位置参数
- 位置参数必须在关键字参数之前
非固定参数传递
有时参数的数量,传递形式(位置,关键字)不可知。
非固定位置参数用*args表示,非固定关键字参数用**kwargs表示。其中:*,**强制要求,args,kwargs 是约定俗称的参数名
*:打包所有位置参数,组成一个元祖,赋值给args;**:打包所有关键字参数,组成一个字典,赋值给kwargs
def stu_register(id, *args, **kwargs): ''' 注册一个学生,打印学生信息 :param id: 学生id :param args: 学生其他信息,采用元祖形式保存 :param kwargs: 学生其他信息,采用字典形式保存 :return: ''' # print(id, *arg, **kwargs) print(id, args, kwargs)#Alex ('男', 22) {'province': 'ShanDong', 'major': '计算机'} stu_register("Alex", '男', 22, province="ShanDong", major="计算机") #传参步骤 # 1.位置参数匹配 id = "Alex" # 2.'男', 22没有固定位置参数匹配,打包成一个元组统一传输给可变位置参数args,至此所有位置参数匹配完毕 # 3.province="ShanDong"关键字参数传递,无:打包成字典,统一传递给可变关键字参数kwargs
解包与不解包
非固定长度参数传递中,存在传递的参数本身为元祖或者字典的情况。
解包:对传递的元祖或者字典处理,分割成单个参数进行传递
非解包:不对传递的元祖或者字典处理,作为一个整体进行传递
def stu_register(id, *args, **kwargs): ''' 注册一个学生,打印学生信息 :param id: 学生id :param args: 学生其他信息,采用元祖形式保存 :param kwargs: 学生其他信息,采用字典形式保存 :return: ''' # print(id, *arg, **kwargs) print(id, args, kwargs) stu_register("Alex", '男', 22, province="ShanDong", major="计算机") #Alex ('男', 22) {'province': 'ShanDong', 'major': '计算机'} #传参步骤 # 1.位置参数匹配 id = "Alex" # 2.'男', 22没有固定位置参数匹配,打包成一个元组统一传输给可变位置参数args,至此所有位置参数匹配完毕 # 3.province="ShanDong"关键字参数传递,无:打包成字典,统一传递给可变关键字参数kwargs #如果,参数本身已经是元祖或者字典形式, 如果需要传递前解包,元祖解包 * ,字典解包 ** tuple_stu_info = ('男', 22, ) #一般都会在最后一个元祖加',' 区分函数调用 dic_stu_info = {'province': "ShanDong", 'major':"计算机"} stu_register("Alex", *tuple_stu_info, **dic_stu_info) #同上:Alex ('男', 22) {'province': 'ShanDong', 'major': '计算机'} #元组不解包,字典解包 stu_register("Alex", tuple_stu_info, **dic_stu_info) # Alex (('男', 22),) {'province': 'ShanDong', 'major': '计算机'} #字典不解包,元组解包 stu_register("Alex", *tuple_stu_info, dic_stu_info) # Alex ('男', 22, {'province': 'ShanDong', 'major': '计算机'}) {} #都不解包 stu_register("Alex", tuple_stu_info, dic_stu_info) # Alex (('男', 22), {'province': 'ShanDong', 'major': '计算机'}) {}
三、局部变量
定义在函数内部的变量称 局部变量,变量定义时没有格式缩进的变量称 全局变量。其中:参数是函数的局部变量
变量作用域
变量可以被使用的范围,即变量的有效范围
- L(Local) 局部作用域
- E(Enclosing) 嵌套函数外的函数中
- G(Global)全局作用域
- B(Built-in)内建作用域
查找顺序: 按照 L –> E –> G –>B依次寻找
name = "刚娘" def weihou(): name = "沉着" weihou_name ="燥起来" print(name,weihou_name) def weiweihou(): global name #只改全局,不改局部 name = "冷静" nonlocal weihou_name weihou_name = "李云龙" # nolocal name #报错: global ,nolocal 起指示作用,变量名还是唯一的 weiweihou() print(name,weihou_name) print(name) weihou() print(name) # # 刚娘 # 沉着 燥起来 # 沉着 李云龙 # 冷静
四、返回值
要想获取函数的执行结果,就可以用return语句把结果返回
注意:
- 函数在执行过程中只要遇到return语句,就会停止执行并返回结果,可以理解为 return 语句代表着函数的结束
- 如果未在函数中指定return,那这个函数的返回值为None
五、嵌套函数
函数定义中有另外一个函数的定义
六、递归
在函数内部,可以调用其他函数。如果一个函数在内部调用自身,这个函数就是递归函数。
# 汉诺塔的移动可以用递归函数非常简单地实现。 # # 请编写move(n, a, b, c)函数,它接收参数n,表示3个柱子A、B、C中第1个柱子A的盘子数量,然后打印出把所有盘子从A借助B移动到C的方法,例如: def move(n, a, b, c): if n == 1: print(a,'--->',c) return move(n-1,a,c,b) move(1,a,b,c) move(n-1,b,a,c) # 期待输出: # A --> C # A --> B # C --> B # A --> C # B --> A # B --> C # A --> C move(3, 'A', 'B', 'C')
尾递归
递归函数逻辑表达清晰,但太耗内存(不断保存上一次栈的状态)。变相提出了尾递归,即在返回的时候计算出下次函数被调用的值替代表达式。
#递归 def jiecheng(n): if n == 1: return 1 else: return n * jiecheng(n-1) #返回值有同名函数表达式 #尾递归 def jiecheng2(n,product=1): if n == 1: return product else: return jiecheng2(n-1,n*product) #无函数表达式,理论上可以单独调用。但python没有优化 print(jiecheng2(10000)) # RecursionError: maximum recursion depth exceeded in comparison
七、匿名函数
匿名函数就是不需要显式定义的函数,用于临时使用的简易逻辑函数
#求一个数的平方 cal = lambda x:x**2 print(cal(5)) # 25
八、高阶函数
函数的参数or返回值是一个函数,这种函数就称之为高阶函数。常用于装饰器
#高阶函数 def cal(f, x, y): return f(x, y) def add(x, y): return x + y def div(x, y): return divmod(x, y) print(cal(add, 2, 3)) # 5 print(cal(div, 5, 3)) # (1, 2)
九、高级函数
函数递归、高阶函数、高级函数是函数式编程的主要特色,原则:不要重复造轮子。 本小节主要介绍 filter、map、functools.reduce以及for 推导式
1.找到长度不唯一的单词,并首字母大写,用,连接成字符串输出 employees = ['neal','s','stu','j','rich','bob','aiden','j','ethan','liam'] l = ','.join(map(lambda i:i.capitalize(),filter(lambda i:len(i)>1,employees))) print(l) # Neal,Stu,Rich,Bob,Aiden,Ethan,Liam #1.2 print(','.join(i.capitalize() for i in employees if len(i)>1)) #更加方便 #2. [‘adam’, ‘LISA’, ‘barT’]转换首字母大写,其他小写 l = ['adam', 'LISA', 'barT'] l = list(map(lambda i:i.capitalize(),l)) print(l) #2.1 print([i.capitalize() for i in l]) #3.[1,3,5,6,7,10,2,5] 求乘积 l = [1,3,5,6,7,10,2,5] import functools print(functools.reduce(lambda x,y:x*y,l)) #4.求5000..10000 之间的回数 print([i for i in range(5000,10000) if str(i)== str(i)[::-1]]) #5. 1. 100 整除2 不能整除4的数,做平方 print(list(map(lambda i:i**2,filter(lambda x:x%2==0 and x%4 !=0,[i for i in range(100)])))) #5.2 for 列表形式。简单元素处理可以取代 map ,filter(if) print([i**2 for i in range(100)if i%2==0 and i%4!=0]) #取最长单词 s = ('dfsa','dsafdsaffdf','sad','adfdsaf') functools.reduce(lambda a,b:a if len(a)>len(b) else b,s)
十、内置函数
Python解释器的内置函数(无论什么地方,什么时间都可以使用)