资源简介 (共96张PPT)函数Chap6 FunctionThere is a road in the mountains of books, and diligence is the path找前5个默尼森数P是素数且M也是素数,并且满足等式M=2P-1,则称M为默尼森数例如P=5,M=2P-1=31,5和31都是素数,因此31是默尼森数。2函数3较大规模的程序通常会被划分成一个个功能模块,这些功能模块就是函数(function)函数的概念6.14函数5函数是一个独立的代码块在解决大规模问题时采用“模块化”策略,将一个大而复杂的原始任务分解为多个较简单的子任务,再为每个简单的子任务设计算法将描述其算法的一组语句封装为一个独立代码块,为每个独立代码块定义一个名字以及能与其他独立代码块通信的接口,这种独立的代码块定义就是函数。找前5个默尼森数P是素数且M也是素数,并且满足等式M=2P-1,则称M为默尼森数例如P=5,M=2P-1=31,5和31都是素数,因此31是默尼森数。6使用函数可以在整体上简化程序结构,降低程序开发和修改的复杂度,提高程序的可读性。Python中的函数7内建函数01第三方库03标准库函数02用户自定义函数04Python中的函数8内建函数指包含在__builtins__模块中的函数,安装完Python后可以直接使用标准库需要先导入模块再使用函数,每个库有相关的一些函数第三方库非常多,是Python重要的特征和优势用户自定义函数有固定的定义、调用和参数传递方式等常用python标准库函数6.29常用Python标准库函数10osremathshutilrandomurllib.requestdatetime已由系统事先定义使用时直接导入后调用:模块名.函数名(参数表)函数名(参数表)6.2.1 os模块中的函数11os模块中常用的处理文件及目录的函数12>>> import os>>> os.getcwd()'C:\\WINDOWS\\system32'>>> path = 'd:\\temp'>>> os.chdir(path)>>> os.getcwd()'d:\\temp'>>> os.rename('current.txt', 'new.txt')>>> os.remove('new.txt')>>> os.mkdir('d:\\temp\\tempdir')>>> os.rmdir('d:\\temp\\tempdir')Sourcedir(os)6.2.2 random模块中的函数13random模块中常用函数的功能和使用方法14>>> import random>>> random.choice(['C++', 'Java', 'Python'])'Java'>>> random.randint(1, 100)37>>> random.randrange(0, 10, 2)4>>> random.random()0.38859914082194214>>> random.uniform(5, 10)5.776718084305783Sourcedir(random)random模块中常用函数的功能和使用方法15>>> import random>>> random.sample(range(100), 10)[16, 49, 26, 6, 61, 64, 29, 28, 34, 72]>>> nums = [1002, 1004, 1001, 1005, 1008]>>> random.shuffle(nums)#将序列随机排列>>> nums[1002, 1008, 1001, 1005, 1004]Source6.2.3 datetime模块中的函数16datetime模块中的函数17>>> from datetime import date>>> date.today()datetime.date(2017, 2, 3)>>> from datetime import time>>> tm = time(23, 20, 35)#创建时间>>> print(tm)23:20:35Sourcedatetime模块中的函数18>>> from datetime import datetime>>> dt = datetime.now()>>> dtdatetime.datetime(2017, 2, 3, 23, 25, 4, 125366)>>> print(dt.strftime('%a, %b %d %Y %H:%M'))Fri, Feb 03 2017 23:27Source形式1 形式2 含义%a %A 星期%b %B 本地月份%d 月份%y %Y 年份%H %I 小时数%M 分钟数dt最后部分包含了毫秒“125366”Datetime.strftime()将对象转换成固定格式的字符串timestamp()和fromtimestamp()19>>> dt = datetime(2017, 2, 3, 23, 29)>>> print(dt)2017-02-03 23:29:00>>> ts = dt.timestamp()#转换成全球统一时间戳>>> ts1486135740.0>>> print(datetime.fromtimestamp(ts))#时间戳转化成本地日期和时间2017-02-03 23:29:00Source函数的定义和调用6.320函数函数调用之前必须先定义21内建函数或标准库函数自定义函数6.3.1 函数的定义22函数的定义23def 函数名([参数表]):'''文档字符串'''函数体语 法表示函数开始,在第一列书写,该行被称为函数首部,用一个冒号结束;函数名是函数的名称,是一个标识符,取名时尽量要做到见名识义;def函数名函数的定义24def 函数名([参数表]):'''文档字符串'''函数体语 法函数名后紧跟一对圆括号(),括号内可以有0个、1个或多个参数,参数间用逗号分隔,这里的参数称为形式参数(简称形参),形参只有被调用后才分配内存空间,调用结束后释放所分配的内存空间;函数体需要缩进,它包含赋值语句和一些功能语句,如果想定义一个什么也不做的函数,函数体可以用pass语句表示。参数表函数体文档字符串是可选的自定义函数的创建>>> def printStr(x):'''print the string'''print(x)Source25一个非常简单的打印一个字符串的函数6.3.2 函数的返回26函数的返回27return 表达式1, 表达式2, …, 表达式n语 法通常会通过return语句将值带回给主调函数位置在函数体内如果是返回多个值,则构成一个元组如果不需要返回任何值,则不用return语句或用return None语句返回值函数的返回28>>> def square(x, y):'''计算参数的和与差'''return x + y, x – ySource返回两个参数的和与差的函数定义6.3.3 函数的调用29函数的返回30函数名([参数表])语 法函数调用时括号中的参数称为实际参数(简称为实参),在函数调用时分配实际的内存空间。如果有多个实参,实参间用逗号分隔。可以没有实参,调用形式为:函数名() 圆括号不能省略。调用时实参将值一一传递给形参,程序执行流程转移到被调用函数,函数调用结束后返回到之前的位置继续执行。调用函数的导入和调用>>> from pStr import printStr>>> printStr('Hi, Python!')Hi, Python!31pStr.pyprintStr()例6.1 编写函数gcd(x, y)求x和y最大公约数32# prog6-1.pydef gcd(x, y):''' calculate the GCD of x and y '''if x < y:x, y = y, xwhile x % y != 0:r = x % yx = yy = rreturn y # 函数返回x = eval(input("Enter the first number: "))y = eval(input("Enter the second number: "))gcdxy = gcd(x, y) # 调用函数print('GCD({0:d}, {1:d}) = {2:d}'.format(x, y, gcdxy))FileOutput:Enter the first number: 16Enter the second number: 24GCD(16, 24) = 8例6.2 求1~100之间的所有素数输出1-100之间的素数# prog6-2 .pyfrom math import sqrtdef isprime(x):if x == 1:return Falsek = int(sqrt(x))for j in range(2, k+1):if x % j == 0:return Falsereturn Truefor i in range(2, 101):if isprime(i):print( i, end = ' ')File33Output:2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97例6.2 求1~100之间的所有素数# prog6-2 .pyfrom math import sqrtdef isprime(x):if x == 1:return Falsek = int(sqrt(x))for j in range(2, k+1):if x%j == 0:return Falsereturn Truefor i in range(2,101):if isprime(i):print( i, end = ' ')File34if __name__ == "__main__":for i in range(2,101):if isprime(i):print( i, end = ' ')File例6.3 编写函数计算平均成绩根据给出的一组学生的3门课成绩信息,编写函数计算每个学生的平均成绩,返回平均成绩最高和最低两位学生的姓名。3536# prog6-3.pydef search(scores):maxScore = 0minScore = 100for k, v in scores.items():aveg = (scores[k][0] + scores[k][1] + scores[k][2]) // 3if aveg >= maxScore:maxScore = avegmaxName = kif aveg <= minScore:minScore = avegminName = kreturn maxName, minNameif __name__ == "__main__":dictScores = {'Jerry' : [87, 85, 91], 'Mary': [76, 83, 88], 'Tim': [97, 95,89], 'John' : [77, 83, 81]}maxName, minName = search(dictScores)print('{0} got the first place, {1} got the last.'.format(maxName, minName))FileLambda函数37def my_add(x, y) : return x + ymy_add = lambda x, y : x + ylambda x, y : x + y>>> my_add(3, 5)8lambda函数又称为匿名函数,即没有具体的函数名lambda函数的目的是让用户快速地定义单行函数,简化用户使用函数的过程。lambda函数匿名函数>>> r = lambda x : x + x>>> r(5)10Source38>>> def addMe2Me(x):'apply operation + to argument'return x + x>>> addMe2Me(5)10Source例6.3 编写函数计算平均成绩——lamba函数39def search(scores):t = sorted(scores.items(), key = lambda d : (d[1][0] + d[1][1] + d[1][2]) // 3)return t[len(t)-1][0], t[0][0]File使用lambda函数确定了排序函数sorted()的参数key,确定了scores.items()的排序关键字例6.3 编写函数计算平均成绩——lambda函数40>>> dScores = {'Jerry' : [87, 85, 91], 'Mary': [76, 83, 88], 'Tim': [97, 95,89], 'John' : [77, 83, 81]}>>> a = sorted(dScores.items() , key = lambda d:d[0])[('Jerry', [87, 85, 91]), ('John', [77, 83, 81]), ('Mary', [76, 83, 88]), ('Tim', [97, 95, 89])]>>> a = sorted(dScores.items() , key = lambda d:d[1][0])[('Mary', [76, 83, 88]), ('John', [77, 83, 81]), ('Jerry', [87, 85, 91]), ('Tim', [97, 95, 89])]File例6.4 模拟一个简易的用户注册和登录系统41# prog6-4.pyaccount = {'Zhangsan': '123456'} # account为全局变量def sign_up():user_name = input("Please input your user name: ")while user_name in account.keys():user_name = input("User name exists, please choose another one:")password = input("Please input your password: ")account[user_name] = passwordprint("Successfully sign up!")File例6.4 模拟一个简易的用户注册和登录系统42def sign_in():user_name = input("Please input your user name: ")if user_name not in account.keys():print("User name not found.")else:count = 0password = input("Please input your password: ")while account[user_name] != password:count += 1if count >= 3 :print("Bye - bye")breakpassword = input("Wrong password, please input again: ")if account[user_name] == password:print("Login success!")File例6.4 模拟一个简易的用户注册和登录系统43if __name__ == '__main__':while True:# 注册0登陆1cmd = input("Sign Up or Sign In Please input 0 or 1:")while cmd != '0' and cmd != '1':print('Wrong command, please input again: ')cmd = input("Sign Up: 0, Sign in: 1")if cmd == '0':sign_up()continueif cmd == '1':sign_in()breakFile例6.4 模拟一个简易的用户注册和登录系统44Output:Sign Up or Sign In Please input 0 or 1:0Please input your user name: LisiPlease input your password: 123456Successfully sign up!Sign Up or Sign In Please input 0 or 1:1Please input your user name: LisiPlease input your password: 123456Login success!__main__模块调用了sign_up()和sign_in()函数。算法设计为注册部分不退出,登录成功或密码输入超过错误次数后退出函数的参数6.445函数参数函数调用时将实参一一传递给形参,通常实参和形参的个数要相同,类型也要相容,否则容易发生错误。参数在调用过程中的变化参数可以设定默认值466.4.1 参数是否可变47实参是否可变的影响48不可变对象可变对象不会影响到实参参数可变or不可变49def change(stringB):stringB = 'Hello, Python! ' stringA = 'Hi, Python!'change(stringA)print(stringA)Filedef change(listB):listB = [4, 5, 6] listA = [1, 2, 3]change(listA)print(listA)File实参不可变实参可变Output:Hi, Python!Output:[1, 2, 3]参数可变or不可变50实参形参[1, 2, 3]实参形参[1, 2, 3][4, 5, 6]不管实参是否可变,在调用时实参和形参都引用了同一对象,但形参在函数中获得新的赋值,所以它又引用了新的对象,因此并不会影响到实参。如何能获得改变过的形参值呢 用return语句将值返回主调函数使用51def change(stringB):stringB = 'Hello, Python! 'return stringB stringA = 'Hi, Python!'print(change(stringA))print(stringA)Filedef change(listB):listB = [4, 5, 6]return listB listA = [1, 2, 3]print(change(listA))print(listA)File修改形参的值对实参的影响52def change(listB):listB[0] = 88return listB listA = [1, 2, 3]print(change(listA))print(listA)FileOutput:[88, 2, 3][88, 2, 3]形参和实参引用同一个对象,形参修改了对象本身的值,引用不变,所以实参的值也跟着发生变化。修改形参的值不影响实参的做法53def change(listB):listB[0] = 88return listB listA = [1, 2, 3]backup_listA = listA[:]# 不能用“backup_listA = listA”语句print(change(listA))print(listA)print(backup_listA)FileOutput:[88, 2, 3][88, 2, 3][1, 2, 3]用“backup_listA = listA[:]”给listA复制一个副本backup_listA,此时listA和backup_listA并不引用同一个对象,调用了change()函数后虽然实参改变了但其副本backup_listA的值不会改变,较为安全。参数可变性结论如果在函数内对形参重新进行了赋值,形参的值改变了不会影响到实参;如果在函数内直接修改形参值,则会影响到实参。54在实际操作过程中可以根据自己的需要确定是否要给实参复制副本,为了安全起见,一般推荐进行复制。虽然在函数内直接修改形参会影响到原来的实参,但有时正是要利用函数参数的这个特性来解决问题。例6.5 将一个列表中所有单词的首字母转成大写55# prog6-5.pydef capital(listB):for i in range(len(listB)):listB[i] = listB[i].capitalize()listA = ["hope", "is", 'a', 'good', 'thing', '.']listA_backup = listA[:]capital(listA)print(listA)FileOutput:['Hope', 'Is', 'A', 'Good', 'Thing', '.']比用return语句返回改变后的列表节省空间,也提高了效率。6.4.2 不同类型的参数561. 位置参数57>>> def printGrade(name, stuID, grade):print("{0}({1})'s grade is {2}.".format(name, stuID, grade))>>> printGrade('Mary', '1002', 'A')Mary(1002)'s grade is A.Source实参写反会怎样?>>> printGrade('A', '1002', 'Mary')A(1002)'s grade is Mary.Source错误的结果2. 关键字参数58>>> def printGrade(name, stuID, grade):print("{0}({1})'s grade is {2}.".format(name, stuID, grade))>>> printGrade(name = 'Jerry', stuID = '1005', grade = 'B')Jerry(1005)'s grade is B.Source关键字参数是让调用者通过使用参数名区分参数。允许改变参数列表中的参数顺序。调用时每个参数的含义更清晰。2. 关键字参数59>>> def f(x , y):'''x and y both correct words or not '''if y:print(x, 'and y both correct ')print(x, 'is OK')>>> f(68, False)68 is OK>>> f(y = False, x = 68)68 is OK>>> f(y = False, 68)SyntaxError: non-keyword arg after keyword arg>>> f(68, y = False)68 is OKSource3. 默认参数在定义函数时给某些参数设定默认值,默认参数以赋值语句的形式给出设定了π的默认值,确定了统一的精度,调用时如果使用默认值则该位置的实参可以省略不写60>>> def area(r, pi = 3.14159):return pi * r *rSource>>> area(3)28.274309999999996Source3. 默认参数默认参数值在调用时可以修改调用时也一样可以利用关键字参数改变参数顺序61>>> area(pi = 3.14, r = 4)50.24Source>>> area(4, 3.14)50.24Source3. 默认参数62>>> def printGrade(name, className = 'Courage', grade):print("{0}({1})'s grade is {2}.".format(name, className, grade))>>> printGrade('Mary', 'A')Source语法错误:非默认参数跟在了默认参数之后默认参数是可以修改的,如果在定义时将默认参数放在非默认参数前面的话,Python解释器无法判断给出的第二个实参'A'是修改了默认参数的值还是对应了后面的参数SyntaxError: non-default argument follows default argument3. 默认参数63>>> def printGrade(name, grade, className = 'Courage'):print("{0}({1})'s grade is {2}.".format(name, className, grade))>>> printGrade('Mary', 'A')Mary(Courage)'s grade is A.Source定义函数时必须将默认参数放在非默认参数的后面。4. 可变长参数Python中允许传递一组数据给一个形参,形参tupleArgs前有一个“*”号,是可变长位置参数的标记,用来收集其余的位置参数,将它们放到一个元组中64>>> def greeting(args1, *tupleArgs):print(args1)print(tupleArgs)Source4. 可变长参数65>>> def greeting(args1, *tupleArgs):print(args1)print(tupleArgs)>>> greeting('Hello,', 'Wangdachuan', 'Liuyun', 'Linling')Hello,('Wangdachuan', 'Liuyun', 'Linling')Source实参中'Hello,'传递给位置参数args1,其余的三个字符串传递给可变长的位置参数tupleArgs,调用后将这组实参放到一个元组中输出。4. 可变长参数66>>> def greeting(args1, *tupleArgs):print(args1)print(tupleArgs)>>> names = ('Wangdachuan', 'Liuyun', 'Linling')>>> greeting('Hello,', *names)Hello,('Wangdachuan', 'Liuyun', 'Linling')Source4. 可变长参数——可变长关键字参数67>>> def assignment(**dictArgs):print(dictArgs)>>> assignment(x = 1, y = 2, z = 3){'x': 1, 'z': 3, 'y': 2}>>> data = {'x': 1, 'z': 3, 'y': 2}>>> assignment(**data){'x': 1, 'z': 3, 'y': 2}Source用两个星号标记可变长的关键字参数。可变长关键字参数允许传入多个(可以是0个)含参数名的参数,这些参数在函数内自动组装成一个字典。也可先将参数名和参数构建成一个字典,然后作为可变长关键字参数传递给函数调用。4. 可变长参数——可变长位置参数和可变长关键字参数68>>> def greeting(args1, *tupleArgs, **dictArgs):print(args1)print(tupleArgs)print(dictArgs)>>> names = ['Wangdachuan', 'Liuyun', 'Linling']>>> info = {'schoolName' : 'NJU', 'City' : 'Nanjing'}>>> greeting('Hello,', *names, **info)Hello,('Wangdachuan', 'Liuyun', 'Linling'){'City': 'Nanjing', 'schoolName': 'NJU'}Source例6.6 实现用户信息注册登记要求必须登记姓名,性别和手机号码,其他如年龄、职业等信息不强制登记。69例6.6 实现用户信息注册登记70# prog6-6.pydef register(name, gender, phonenum, **otherinfo):''' register users information '''print('name: ', name, 'gender: ', gender, 'phone num: ', phonenum)print('other information: ', otherinfo)File例6.6 实现用户信息注册登记71>>> register('Chenqian', 'M', '11111111111')name: Chenqian gender: M phone num: 11111111111other information: {}Source例6.6 实现用户信息注册登记72>>> otherinfo = {'age': 24, 'city': 'Nanjing', 'job':'teacher'}>>> register('Limei', 'F','22222222222', **otherinfo)name: Limei gender: F phone num: 2222222222other information: {'age': 24, 'city': 'Nanjing', 'job': 'teacher'}Source除了name、gender、phonenum外如果没有登记额外的信息,则可变长关键字参数otherinfo收集不到任何参数,调用后输出一个空字典。如果有额外的信息登记,参数传递时这些参数被组装成一个字典。变量的作用域6.573变量作用域74>>> def f(): x = 5>>> f()>>> print(x)Traceback (most recent call last):File "", line 1, in print(x)NameError: name 'x' is not definedSource变量作用域每个变量都有自己的作用域,也就是在某个代码段内使用该变量是合法的,在此代码段外使用该变量则是非法的。除了作为全局作用域的变量外,每次函数调用都会创建一个新的作用域。75对不同作用域同名变量的处理76>>> def f(): x = 5>>> x = 3>>> f()>>> print(x)3Source>>> def f(): y = 5>>> x = 3>>> f()>>> print(x)3Source函数内部使用全局变量77>>> def f():y = 5print(x + y)>>> x = 3>>> f()8Source函数内部虽然可以使用全局变量,但使用要慎重,如果在多个函数内部使用全局变量,则无法确定全局变量某一时刻的值,容易发生错误。局部变量和全局变量同名时78>>> x = 3>>> def f():x = 5print(x ** 2)>>> f()Source在局部变量(包括形参)和全局变量同名时,局部变量屏蔽(Shadowing)全局变量程序执行结果=?函数内部同时出现局部变量和全局变量79>>> x = 3>>> def f():print(x ** 2)x = 5print(x ** 2)>>> f()Traceback (most recent call last):File "", line 1, in f()File "", line 2, in fprint(x **2)UnboundLocalError: local variable 'x' referenced before assignmentSource函数内部同时出现局部变量和全局变量80>>> x = 3>>> def f():global xprint(x ** 2)x = 5print(x ** 2)>>> x = 3>>> f()925Source使用关键字global声明将使用全局变量递归6.681递归82生成斐波那契数列的方法循环递归递归是最能表现计算思维的算法之一函数的嵌套调用83f1()main…Endf2()f1()……f2()……EndEnd①②③④函数间可以相互调用,如果在__main__模块中调用了f1函数,在函数f1中又调用了函数f2,就形成了函数的嵌套调用。直接递归和间接递归84f2(…)f1(…)…f1(…)f2(…)………(a)间接递归调用f1(…)…f1(…)…(b)直接递归调用递归是特殊的嵌套调用,是对函数自身的调用正确的递归调用的要求有一个比原始调用规模小的函数副本;有基本情况即递归终止条件。85def f():f()Source无穷递归(infinite recursion)递归调用的过程每一次递归调用要解决的问题都要比上一次的调用简单,规模较大的问题可以往下分解为若干个规模较小的问题,规模越来越小最终达到最小规模的递归终止条件(基本情况)解决完基本情况后函数沿着调用顺序逐级返回上次调用,直到函数的原始调用处结束一般会包含一个选择结构,条件为真时计算基本情况并结束递归调用,条件为假时简化问题执行副本继续递归调用。86递归递归的执行87系统资源消耗比循环大逐层返回调用至最初层03遇到边界条件停止递归逐层递归调用0201例6.7 编写递归函数计算n的阶乘88n! =1 (当n=1) n×(n-1)! (当n>1)n的阶乘的定义是一种递归定义例6.7 编写递归函数计算n的阶乘89# prog6-7.pydef fac(n):if n == 1:return 1else:return n * fac(n–1)File递归必须要有边界条件,即停止递归的条件例6.7 编写递归函数计算n的阶乘90fac(3)fac(3):if n == 1:return 1else:return 3*fac(3-1)fac(2):if n == 1:return 1else:return 2*fac(2-1)fac(1):if n == 1:return 1else:return 1*fac (1-1)①②④⑤⑥③递归函数的每次调用时系统都为函数的局部变量(包括形参)分配本次调用使用的存储空间,直到本次调用结束返回主调程序时方才释放。递归和迭代求n的阶乘斐波那契数列用二分法求方程的根91经典的Hanoi(汉诺塔)游戏八皇后有明显的迭代方式适合用递归实现例6.8 Hanoi塔问题92ABCHanoi塔问题的描述是:有3个底座(分别标记为A、B和C)各有一根针,A座的针上已从下往上从大到小依次叠放了64个(简单起见用3个盘子表示)大小不同的盘子,要求将A座针上的盘子全部搬到C座的针上。在搬运过程中可以借助于B座的针,每次只能搬一个盘子,任何时候每根针上的盘子都必须保持大盘子在下小盘子在上的叠放顺序。例6.8 Hanoi塔问题64个盘子需要搬运264-1次。当A座的针上只有一个盘子时(即n=1)数据规模最小,依据搬运要求和搬运规则可以很容易搬运这个盘子,因此直接输出搬运该盘子的操作指令后返回。当A座针上有多个盘子时(即n>1),可以将这n个盘子的搬运操作分解为三部分:(1)输出将A座针上的前n-1个盘子借助C座针搬到B座针的搬运指令;(2)输出将A座针上剩下的最后一个盘子(编号为n)直接从A座针搬到C座针的搬运指令;(3)输出将B座针上的n-1个盘子借助A座针搬到C座针的搬运指令。93例6.8 Hanoi塔问题94Output:input the number of plates: 4a -> ba -> cb -> ca -> bc -> ac -> ba -> ba -> cb -> cb -> ac -> ab -> ca -> ba -> cb -> c# Filename: 6-8.pydef hanoi(a,b,c,n):if n==1:print(a,'->',c)else:hanoi(a,c,b,n-1)print(a,'->', c)hanoi(b,a,c,n-1)n = int(input('input the number of plates: '))hanoi('a', 'b', 'c', n)File递归深度的设定查看递归深度>>> import sys>>> sys. getrecursionlimit()1000手工修改默认值sys.setrecursionlimit(20000)95小结函数的概念常用Python标准库函数函数的定义和调用函数的参数变量的作用域递归函数96 展开更多...... 收起↑ 资源预览