一、函数介绍
- 什么是函数
请看如下代码:
print(" _ooOoo_ ") print(" o8888888o ") print(" 88 . 88 ") print(" (| -_- |) ") print(" O\\ = /O ") print(" ____/`---'\\____ ") print(" . ' \\| |// `. ") print(" / \\||| : |||// \\ ") print(" / _||||| -:- |||||- \\ ") print(" | | \\\\\\ - /// | | ") print(" | \\_| ''\\---/'' | | ") print(" \\ .-\\__ `-` ___/-. / ") print(" ___`. .' /--.--\\ `. . __ ") print(" ."" '< `.___\\_<|>_/___.' >'"". ") print(" | | : `- \\`.;`\\ _ /`;.`/ - ` : | | ") print(" \\ \\ `-. \\_ __\\ /__ _/ .-` / / ") print(" ======`-.____`-.___\\_____/___.-`____.-'====== ") print(" `=---=' ") print(" ") print(" ............................................. ") print(" 佛祖镇楼 BUG辟易 ") print(" 佛曰: ") print(" 写字楼里写字间,写字间里程序员; ") print(" 程序人员写程序,又拿程序换酒钱。 ") print(" 酒醒只在网上坐,酒醉还来网下眠; ") print(" 酒醉酒醒日复日,网上网下年复年。 ") print(" 但愿老死电脑间,不愿鞠躬老板前; ") print(" 奔驰宝马贵者趣,公交自行程序员。 ") print(" 别人笑我忒疯癫,我笑自己命太贱; ") print(" 不见满街漂亮妹,哪个归得程序员? ")
运行后的现象:
- 想一想:
如果一个程序在不同的地方需要输出“佛祖镇楼”,程序应该怎样设计?
if 条件1: 输出‘佛祖镇楼’ ...(省略)... if 条件2: 输出‘佛祖镇楼’ ...(省略)...
如果需要输出多次,是否意味着要编写这块代码多次呢?
- 小总结:
如果在开发程序时,需要某块代码多次,但是为了提高编写的效率以及代码的重用,所以把具有独立功能的代码块组织为一个小模块,这就是函数。
二、函数定义、调用
- 定义函数
定义函数的格式如下:
def 函数名(): 代码
- demo:
# 定义一个函数,能够完成打印信息的功能 def printInfo(): print('------------------------------------') print( ' 人生苦短,我用Python') print( '------------------------------------')
- 调用函数
- 定义了函数之后,就相当于有了一个具有某些功能的代码,想要让这些代码能够执行,需要调用它
- 调用函数很简单的,通过 函数名() 即可完成调用
- demo:
# 定义一个函数,能够完成打印信息的功能 def printInfo(): print('------------------------------------') print(' 人生苦短,我用Python') print('------------------------------------') # 定义完函数后,函数是不会自动执行的,需要调用它才可以 printInfo()
结果:
- 练一练
- 要求:定义一个函数,能够输出自己的姓名和年龄,并且调用这个函数让它执行
- 使用def定义函数
- 编写完函数之后,通过 函数名() 进行调用
三、函数的文档说明
def test(a,b):#定义一个函数 ''' 用来完成对2个数求和 ''' print("%d"%(a+b)) test(11,22)#调用函数 help(test)#能够看到test函数的相关说明
结果:
四、函数参数(一)
思考一个问题,如下:
- 现在需要定义一个函数,这个函数能够完成2个数的加法运算,并且把结果打印出来,该怎样设计?下面的代码可以吗?有什么缺陷吗?
def add2num(): a = 11 b = 22 c = a+b print (c)
- 为了让一个函数更通用,即想让它计算哪两个数的和,就让它计算哪两个数的和,在定义函数的时候可以让函数接收数据,就解决了这个问题,这就是 函数的参数。
- 定义带有参数的函数
- 示例如下:
def add2num(a, b): c = a+b print (c)
- 调用带有参数的函数
- 以调用上面的add2num(a, b)函数为例:
def add2num(a, b): c = a + b print(c) add2num(110, 22) # 调用带有参数的函数时,需要在小括号中,传递数据
调用带有参数函数的运行过程:
结果:
- 练一练
- 要求:定义一个函数,完成前2个数完成加法运算,然后对第3个数,进行减法;然后调用这个函数
- 使用def定义函数,要注意有3个参数
- 调用的时候,这个函数定义时有几个参数,那么就需要传递几个参数
- 调用函数时参数的顺序
- demo:
def test(a, b): print(a,b) test(1,2)#调用 print('-----------') test(b = 1,a = 2)#调用
结果:
- 小总结
- 定义时小括号中的参数,用来接收参数用的,称为 “形参”
- 调用时小括号中的参数,用来传递给函数用的,称为 “实参”
五、函数返回值(一)
- “返回值”介绍
- 现实生活中的场景:
你爸给你10块钱,让你给他买包烟。这个例子中,10块钱是你爸给你的,就相当于调用函数时传递的参数,让你买烟这个事情最终的目标是,让你把烟给你爸带回来对么,,,此时烟就是返回值。
- 开发中的场景:
定义了一个函数,完成了获取室内温度,想一想是不是应该把这个结果给调用者,只有调用者拥有了这个返回值,才能够根据当前的温度做适当的调整。
- 综上所述:
所谓“返回值”,就是程序中函数完成一件事情后,最后给调用者的结果。
- 带有返回值的函数
- 想要在函数中把结果返回给调用者,需要在函数中使用return
- 如下示例:
def add2num(a, b): c = a + b return c
- 或者
def add2num(a, b): return a + b
- 保存函数的返回值
在本小节刚开始的时候,说过的“买烟”的例子中,最后你给你爸烟时,你爸一定是从你手中接过来对么,程序也是如此,如果一个函数返回了一个数据,那么想要用这个数据,那么就需要保存。
- 保存函数的返回值示例如下:
#定义函数 def add2num(a, b): return a+b #调用函数,顺便保存函数的返回值 result = add2num(100,98) #因为result已经保存了add2num的返回值,所以接下来就可以使用了 print(result)
结果:
六、4种函数的类型
函数根据有没有参数,有没有返回值,可以相互组合,一共有4种:
- 无参数,无返回值
- 无参数,无返回值
- 有参数,无返回值
- 有参数,有返回值
- 无参数,无返回值的函数
此类函数,不能接收参数,也没有返回值,一般情况下,打印提示灯类似的功能,使用这类的函数- demo:
def printMenu(): print('--------------------------') print(' xx涮涮锅 点菜系统') print('') print(' 1. 羊肉涮涮锅') print(' 2. 牛肉涮涮锅') print(' 3. 猪肉涮涮锅') print('--------------------------') printMenu()
结果:
- 无参数,有返回值的函数
此类函数,不能接收参数,但是可以返回某个数据,一般情况下,像采集数据,用此类函数- demo:
# 获取温度 def getTemperature(): #这里是获取温度的一些处理过程 #为了简单起见,先模拟返回一个数据 return 24 temperature = getTemperature() print('当前的温度为:%d'%temperature)
结果:
- 有参数,无返回值的函数
此类函数,能接收参数,但不可以返回数据,一般情况下,对某些变量设置数据而不需结果时,用此类函数
- 有参数,有返回值的函数
此类函数,不仅能接收参数,还可以返回某个数据,一般情况下,像数据处理并需要结果的应用,用此类函数- demo:
# 计算1~num的累积和 def calculateNum(num): result = 0 i = 1 while i <= num: result = result + i i += 1 return result result = calculateNum(100) print('1~100的累积和为:%d' % result)
结果:
- 小总结
- 函数根据有没有参数,有没有返回值可以相互组合
- 定义函数时,是根据实际的功能需求来设计的,所以不同开发人员编写的函数类型各不相同
七、函数的嵌套调用
- demo:
def testB(): print('---- testB start----') print('这里是testB函数执行的代码...(省略)...') print('---- testB end----') def testA(): print('---- testA start----') testB() print('---- testA end----') testA()
结果:
- 小总结:
一个函数里面又调用了另外一个函数,这就是所谓的函数嵌套调用
- 如果函数A中,调用了另外一个函数B,那么先把函数B中的任务都执行完毕之后才会回到上次 函数A执行的位置
八、函数应用:打印图形和数学计算
- 目标
- 感受函数的嵌套调用
- 感受程序设计的思路,复杂问题分解为简单问题
- 思考&实现1
- 写一个函数打印一条横线
- 打印自定义行数的横线
- 参考代码1:
# 打印一条横线 def printOneLine(): print("-"*30) # 打印多条横线 def printNumLine(num): i=0 # 因为printOneLine函数已经完成了打印横线的功能, # 只需要多次调用此函数即可 while i<num: printOneLine() i+=1 printNumLine(3)
结果:
- 思考&实现2
- 写一个函数求三个数的和
- 写一个函数求三个数的平均值
- 参考代码2
# 求3个数的和 def sum3Number(a,b,c): return a+b+c # return 的后面可以是数值,也可是一个表达式 # 完成对3个数求平均值 def average3Number(a,b,c): # 因为sum3Number函数已经完成了3个数的就和,所以只需调用即可 # 即把接收到的3个数,当做实参传递即可 sumResult = sum3Number(a,b,c) aveResult = sumResult/3.0 return aveResult # 调用函数,完成对3个数求平均值 result = average3Number(11,2,55) print("average is %d"%result)
结果:
九、局部变量
- 什么是局部变量
如下图所示:
- 小总结
- 局部变量,就是在函数内部定义的变量
- 其作用范围是这个函数内部,即只能在这个函数中使用,在函数的外部是不能使用的
- 因为其作用范围只是在自己的函数内部,所以不同的函数可以定义相同名字的局部变量(打个比方,把你、我是当做成函数,把局部变量理解为每个人手里的手机,你可有个iPhone,我当然也可以有个iPhone了, 互不相关)
- 局部变量的作用,为了临时保存数据需要在函数中定义变量来进行存储
当函数调用时,局部变量被创建,当函数调用完成后这个变量就不能够使用了
十、全局变量
- 什么是全局变量
如果一个变量,既能在一个函数中使用,也能在其他的函数中使用,这样的变量就是全局变量
- 打个比方:有2个兄弟 各自都有手机,各自有自己的小秘密在手机里,不让另外一方使用(可以理解为局部变量);但是家里的电话是2个兄弟都可以随便使用的(可以理解为全局变量)
- demo:
# 定义全局变量 a = 100 def test1(): print(a) # 虽然没有定义变量a但是依然可以获取其数据 def test2(): print(a) # 虽然没有定义变量a但是依然可以获取其数据 # 调用函数 test1() test2()
结果:
- 总结1:
在函数外边定义的变量叫做全局变量
全局变量能够在所有的函数中进行访问
- 全局变量和局部变量名字相同问题
- 总结2:
当函数内出现局部变量和全局变量相同名字时,函数内部中的 变量名 = 数据 此时理解为定义了一个局部变量,而不是修改全局变量的值
- 修改全局变量
函数中进行使用时可否进行修改呢?
代码如下:
- 总结3:
如果在函数中出现global 全局变量的名字 那么这个函数中即使出现和全局变量名相同的变量名 = 数据 也理解为对全局变量进行修改,而不是定义局部变量- 如果在一个函数中需要对多个全局变量进行修改,那么可以使用
# 可以使用一次global对多个全局变量进行声明 global a, b # 还可以用多次global声明都是可以的 # global a # global b
十一、多函数程序的基本使用流程
一般在实际开发过程中,一个程序往往由多个函数(后面知识中会讲解类)组成,并且多个函数共享某些数据,这种场景是经常出现的,因此下面来总结下,多个函数中共享数据的几种方式
- 使用全局变量
- demo:
g_num = 0 def test1(): global g_num #将处理结果存储到全局变量g_num中 def test2(): #通过获取全局变量g_num的值, 从而获取test1函数处理之后的结果 # 1. 先调用test1得到数据并且存到全局变量中 test1() # 2. 再调用test2,处理test1函数执行之后的这个值 test2()
- 使用函数的返回值、参数
- demo:
def test1(): #通过return将一个数据结果返回 def test2(num): #通过形参的方式保存传递过来的数据,就可以处理了 # 1. 先调用test1得到数据并且存到变量result中 result = test1() # 2. 调用test2时,将result的值传递到test2中,从而让这个函数对其进行处理 test2(result)
- 函数嵌套调用
- demo:
def test1(): #通过return将一个数据结果返回 def test2(): # 1. 先调用test1并且把结果返回来 result = test1() # 2. 对result进行处理 # 1. 调用test1时,完成所有的处理 test2()
十二、函数返回值(二)
在python中我们怎样返回多个值?
- 多个return?
def create_nums(): print("---1---") return 1 # 函数中下面的代码不会被执行,因为return除了能够将数据返回之外,还有一个隐藏的功能:结束函数 print("---2---") return 2 print("---3---")
总结1:
- 一个函数中可以有多个return语句,但是只要有一个return语句被执行到,那么这个函数就会结束了,因此后面的return没有什么用处
- 如果程序设计为如下,是可以的因为不同的场景下执行不同的return
def create_nums(num): print("---1---") if num == 100: print("---2---") return num + 1 # 函数中下面的代码不会被执行,因为return除了能够将数据返回之外,还有一个隐藏的功能:结束函数 else: print("---3---") return num + 2 print("---4---") result1 = create_nums(100) print(result1) # 打印101 result2 = create_nums(200) print(result2) # 打印202
结果:
- 一个函数返回多个数据的方式
- demo:
def divid(a, b): shang = a//b yushu = a%b return shang, yushu #默认是元组 result = divid(5, 2) print(result) # 输出(2, 1)
结果:
总结2:
- return后面可以是元组,列表、字典等,只要是能够存储多个数据的类型,就可以一次性返回多个数据
- 如果return后面有多个数据,那么默认是元组
十三、函数参数(二)
- 缺省参数
- 调用函数时,缺省参数的值如果没有传入,则取默认值。
- 下例会打印默认的age,如果age没有被传入:
- demo:
def printinfo(name, age=35): # 打印任何传入的字符串 print("name: %s" % name) print("age %d" % age) # 调用printinfo函数 printinfo(name="miki") # 在函数执行过程中 age去默认值35 printinfo(age=9 ,name="miki")
结果:
- 总结1:
在形参中默认有值的参数,称之为缺省参数
注意:带有默认值的参数一定要位于参数列表的最后面
- 不定长参数
- 有时可能需要一个函数能处理比当初声明时更多的参数, 这些参数叫做不定长参数,声明时不会命名。
- 基本语法如下:
def functionname([formal_args,] *args, **kwargs): """函数_文档字符串""" function_suite return [expression]
- demo:
""" 可变参数演示示例 """ def fun(a, b, *args, **kwargs): print("a =%d" % a) print("b =%d" % b) print("args:") print(args) print("kwargs: ") for key, value in kwargs.items(): print("key=%s" % value) fun(1, 2, 3, 4, 5, m=6, n=7, p=8) # 注意传递的参数对应
结果:
- 注意:
- 加了一个星号的变量args会存放所有未命名的变量参数,args为元组
- 而加两个星号的变量kwargs会存放命名参数,即形如key=value的参数, kwargs为字典.
- 缺省参数在*args后面
- demo:
def sum_nums_3(a, *args, b=22, c=33, **kwargs): print(a) print(b) print(c) print(args) print(kwargs) sum_nums_3(100, 200, 300, 400, 500, 600, 700, b=1, c=2, mm=800, nn=900)
结果:
- 说明:
如果很多个值都是不定长参数,那么这种情况下,可以将缺省参数放到 *args的后面, 但如果有**kwargs的话,**kwargs必须是最后的
十四、拆包、交换变量的值
- 对返回的数据直接拆包
- demo:
def get_my_info(): high = 178 weight = 100 age = 18 return high, weight, age result = get_my_info() print(result) print('-----------------') my_high, my_weight, my_age = get_my_info() print(my_high) print(my_weight) print(my_age)
结果:
- 总结:
- 拆包时要注意,需要拆的数据的个数要与变量的个数相同,否则程序会异常
- 除了对元组拆包之外,还可以对列表、字典等拆包
- 交换2个变量的值
- demo:
# 第1种方式 # a = 4 # b = 5 # c = 0 # # print(a) # print(b) # print('-----') # c = a # a = b # b = c # # print(a) # print(b) # 第2种方式 # a = 4 # b = 5 # print(a) # print(b) # print('-----') # a = a+b # a=9, b=5 # b = a-b # a=9, b=4 # a = a-b # a=5, b=4 # print(a) # print(b) # 第3种方式 a, b = 4, 5 print(a) print(b) print('-----') a, b = b, a print(a) print(b)
结果:
十五、引用(一)
- 想一想
>>> a = 1 >>> b = a >>> b 1 >>> a = 2 >>> a 2
请问此时b的值为多少?
>>> a = [1, 2] >>> b = a >>> b [1, 2] >>> a.append(3) >>> a [1, 2, 3]
请问此时b的值又是多少?
- 引用
- 在python中,值是靠引用来传递来的。
- 我们可以用id()来判断两个变量是否为同一个值的引用。 我们可以将id值理解为那块内存的地址标示。
>>> a = 1 >>> b = a >>> id(a) 13033816 >>> id(b) # 注意两个变量的id值相同 13033816 >>> a = 2 >>> id(a) # 注意a的id值已经变了 13033792 >>> id(b) # b的id值依旧 13033816 >>> a = [1, 2] >>> b = a >>> id(a) 139935018544808 >>> id(b) 139935018544808 >>> a.append(3) >>> a [1, 2, 3] >>> id(a) 139935018544808 >>> id(b) # 注意a与b始终指向同一个地址 139935018544808
- 总结:
之前为了更好的理解变量,咱们可以把a=100理解为变量a中存放了100,事实上变量a存储是100的引用(可理解为在内存中的一个编号)
十六、可变、不可变类型
- 总结
- 所谓可变类型与不可变类型是指:数据能够直接进行修改,如果能直接修改那么就是可变,否则是不可变
- 可变类型有: 列表、字典、集合
- 不可变类型有: 数字、字符串、元组
十七、引用(二)
- 引用当做实参
- 可变类型与不可变类型的变量分别作为函数参数时,会有什么不同吗?
- Python有没有类似C语言中的指针传参呢?
- demo:
def test1(b): # 变量b一定是一个局部变量,就看它指向的是谁?可变还是不可变 b += b # += 是直接对b指向的空间进行修改,而不是让b指向一个新的 # b = b+b # xx = xx+yyy 先把=号右边的结果计算出来,然后让b指向这个新的地方,不管原来b指向谁 # 现在b一定指向这个新的地方 # a = [11, 22] a = 100 print(id(a)) test1(a) print(a) print(id(a))
结果:
- 总结:
- Python中函数参数是引用传递(注意不是值传递)
- 对于不可变类型,因变量不能修改,所以运算不会影响到变量自身
- 而对于可变类型来说,函数体中的运算有可能会更改传入的参数变量
十八、函数应用:学生管理系统
import time # import os # 定一个列表,用来存储所有的学生信息(每个学生是一个字典) info_list = [] def print_menu(): print("---------------------------") print(" 学生管理系统 V1.0") print(" 1:添加学生") print(" 2:删除学生") print(" 3:修改学生") print(" 4:查询学生") print(" 5:显示所有学生") print(" 6:退出系统") print("---------------------------") def add_new_info(): """添加学生信息""" global info_list new_name = input("请输入姓名:") new_tel = input("请输入手机号:") new_qq = input("请输入QQ:") for temp_info in info_list: if temp_info['name'] == new_name: print("此用户名已经被占用,请重新输入") return # 如果一个函数只有return就相当于让函数结束,没有返回值 # 定义一个字典,用来存储用户的学生信息(这是一个字典) info = {} # 向字典中添加数据 info["name"] = new_name info["tel"] = new_tel info["qq"] = new_qq # 向列表中添加这个字典 info_list.append(info) def del_info(): """删除学生信息""" global info_list del_num = int(input("请输入要删除的序号:")) if 0 <= del_num < len(info_list): del_flag = input("你确定要删除么?yes or no") if del_flag == "yes": del info_list[del_num] else: print("输入序号有误,请重新输入") def modify_info(): """修改学生信息""" global info_list modify_num = int(input("请输入要修改的序号:")) if 0 <= modify_num < len(info_list): print("你要修改的信息是:") print("name:%s, tel:%s, QQ:%s" % (info_list[modify_num]['name'], info_list[modify_num]['tel'],info_list[modify_num]['qq'])) info_list[modify_num]['name'] = input("请输入新的姓名:") info_list[modify_num]['tel'] = input("请输入新的手机号:") info_list[modify_num]['qq'] = input("请输入新QQ:") else: print("输入序号有误,请重新输入") def search_info(): """查询学生信息""" search_name = input("请输入要查询的学生姓名:") for temp_info in info_list: if temp_info['name'] == search_name: print("查询到的信息如下:") print("name:%s, tel:%s, QQ:%s" % (temp_info['name'], temp_info['tel'], temp_info['qq'])) break else: print("没有您要找的信息....") def print_all_info(): """遍历学生信息""" print("序号\t姓名\t\t手机号\t\tQQ") i = 0 for temp in info_list: # temp是一个字典 print("%d\t%s\t\t%s\t\t%s" % (i, temp['name'], temp['tel'], temp['qq'])) i += 1 def main(): """用来控制整个流程""" while True: # 1. 打印功能 print_menu() # 2. 获取用户的选择 num = input("请输入要进行的操作(数字)") # 3. 根据用户选择,做相应的事情 if num == "1": # 添加学生 add_new_info() elif num == "2": # 删除学生 del_info() elif num == "3": # 修改学生 modify_info() elif num == "4": # 查询学生 search_info() elif num == "5": # 遍历所有的信息 print_all_info() elif num == "6": # 退出系统 exit_flag = input("亲,你确定要退出么?~~~~(>_<)~~~~(yes or no) ") if exit_flag == "yes": break else: print("输入有误,请重新输入......") input("\n\n\n按回车键继续....") # os.system("clear") # 调用Linux命令clear完成清屏 # 程序的开始 main()
结果:
十九、递归函数
- 什么是递归函数
- 通过前面的学习知道一个函数可以调用其他函数。
如果一个函数在内部不调用其它的函数,而是自己本身的话,这个函数就是递归函数。
二十、匿名函数
用lambda关键词能创建小型匿名函数。这种函数得名于省略了用def声明函数的标准步骤。
lambda函数的语法只包含一个语句,如下:lambda [arg1 [,arg2,.....argn]]:expression
- demo:
sum = lambda arg1, arg2: arg1 + arg2 #调用sum函数 print("Value of total : ", sum( 10, 20 )) print("Value of total : ", sum( 20, 20 ))
结果:
Lambda函数能接收任何数量的参数但只能返回一个表达式的值
匿名函数不能直接调用print,因为lambda需要一个表达式
- 应用场合
- 函数作为参数传递
- 自己定义函数
- 作为内置函数的参数
二十一、函数使用注意事项
- 自定义函数
- 无参数、无返回值 def 函数名(): 语句
- 无参数、有返回值
def 函数名(): 语句 return 需要返回的数值
- 注意:
- 一个函数到底有没有返回值,就看有没有return,因为只有return才可以返回数据
- 在开发中往往根据需求来设计函数需不需要返回值
- 函数中,可以有多个return语句,但是只要执行到一个return语句,那么就意味着这个函数的调用完成
- 有参数、无返回值
def 函数名(形参列表): 语句
- 注意:
- 在调用函数时,如果需要把一些数据一起传递过去,被调用函数就需要用参数来接收
- 参数列表中变量的个数根据实际传递的数据的多少来确定
- 有参数、有返回值
def 函数名(形参列表): 语句 return 需要返回的数值
- 函数名不能重复
- 调用函数
- 调用的方式为:
函数名([实参列表])
- 调用时,到底写不写 实参
如果调用的函数 在定义时有形参,那么在调用的时候就应该传递参数- 调用时,实参的个数和先后顺序应该和定义函数中要求的一致
- 如果调用的函数有返回值,那么就可以用一个变量来进行保存这个值
- 作用域
- 在一个函数中定义的变量,只能在本函数中用(局部变量)
- 在函数外定义的变量,可以在所有的函数中使用(全局变量)