第五章 函数和代码复用

5.1 函数的基本使用

5.1.1 函数的定义

定义:函数是一段具有特定功能的、可重用的语句组,用函数名来表示并通过函数名进行功能调用。

使用函数的目的:降低编程难度和代码重用。

函数也可以看作是一段具有名字的子程序,可以在需要的地方调用执行,不需要在每个执行的地方重复编写这些语句。严格地说,函数一种功能抽象。

有些函数是用户自己编写的,称为自定义函数;Python安装包也自带了一些函数和方法,包括Python内置的函数(如abs()、eval())、Python标准库中的函数(如math库中的sqrt()等)

Python使用def保留字定义一个函数,语法形式如下:

def <函数名>(<参数列表>):<函数体>return<返回值列表>

函数名:可以是任何有效的Python标识符

参数列表:调用该函数时传递给它的值,可以有零个、一个或多个,当传递多个参数时各参数逗号分隔,当没有参数时也要保留圆括号。

形式参数:函数定义中参数列表里面的参数是形式参数,简称为”形参“。

函数体:函数每次被调用时执行的代码,由一行或多行语句组成。

当需要返回值使,使用保留字return和返回值列表,否则函数可以没有return语句,在函数体技术位置将控制权返回给调用者。

函数调用和执行的一般形式如下:

<函数名>(<参数列表>)

实际参数:此时,参数列表中给出要传入函数内部的参数,简称“实参”。

例5.1 生日歌

过生日时要为朋友唱生日歌,歌词为

Happy birthday to you!

Happy birthday to you!

Happy birthday, dear<名字>

Happy birthday to you!

编写程序为Mike和Lily输出生日歌。最简单的实现方式是重复使用print()语句,对Mike的生日歌输出如下:

print("Happy birthday to you!")
print("Happy birthday to you!")
print("Happy birthday, dear Mike!")
print("Happy birthday to you!")

结果:

Happy birthday to you!
Happy birthday to you!
Happy birthday, dear Mike!
Happy birthday to you!

为了能够复用语句,考虑将代码修改为:

#m5.1HappyBirthday.py
def happy():print("Happy birthday to you!")def happyB(name):happy()happy()print("Happy birthday, dear {}!".format(name))happy()happyB("Mike")
print()
happyB("Lily")

第5行中定义了一个函数happyB(),包括中的name是形参,用来指代要输入函数的实际变量,并参与完成函数内部功能。第11行和第13行调用两次happyB()函数,输入的”Mike“和”Lily“是实参,替换name,用于函数执行。类似执行了如下语句

name = "Mike"

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Lf4cUEYd-1593708789904)(https://i.loli.net/2020/05/20/1MbXml54JdvenEj.png)]

程序输出如下:

>>>
Happy birthday to you!
Happy birthday to you!
Happy birthday, dear Mike!
Happy birthday to you!Happy birthday to you!
Happy birthday to you!
Happy birthday, dear Lily!
Happy birthday to you!

5.1.2 函数的调用过程

程序调用一个函数需要执行以下4个步骤。

(1)调用程序在调用处暂停执行。

(2)在调用时将实参复制给函数的形参。

(3)执行函数体语句。

(4)函数调用结束给出返回值,程序回到调用前的暂停处继续执行。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mdmOCgfV-1593708789907)(https://i.loli.net/2020/05/20/aKz6ilULAwC4oN3.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VVolygho-1593708789909)(https://i.loli.net/2020/05/20/ilugR9QbWDO2eVN.png)]


拓展:函数式编程

函数式编程是一种编程范式,常见的编程范式还包括命令式编程和面向对象编程等。函数式编程的主要思想是把程序过程尽量写成一系列函数调用,通过函数进一步提高封装级别。函数式编程通过使用一系列函数能够使代码编写更简洁、更易于理解,是中小规模软件项目中最常见的编程方式。


5.1.3 lambada函数

lambada:是python的33个保留字中的其中一个,该保留字用于定义一种特殊的函数——匿名函数,又称lambda函数。

匿名函数并非没有名字,而是将函数名作为函数结果返回,语法格式如下:

<函数名> = lambda <参数列表>: <表达式>

lambda函数与正常函数一样,等价于下面形式:

def <函数名>(<参数列表>):return <表达式>

简单地说,lambda函数用于定义简单的、能够在一行内表示的函数,返回一个函数类型,实例如下:

>>> f = lambda x, y: x + y
>>> type(f)
<class 'function'>
>>> f(10, 12)
22

lambda函数用于需要函数对象的场景。

思考与练习

5.1 Python中定义的函数的关键字是(A)

A.def B.define C.function D.defunc

5.2 下列不是使用函数的优点的是(D)

A.减少代码重复 B.使程序更加模块化 C.使程序便于阅读 D.为了展现智力优势

5.3 判断题:函数在调用前不需要定义,拿来即用就好 X

5.4 下面Python程序中定义f1()时还没有定义f2(),这种函数调用是否合法?

def f1():f2()
def f2():print("函数f2()")
f1()

答:合法。因为Python语言是解析执行,即只要在真正调用函数之前定义函数,都可以进行合法调用。

MOOC在线小测:

1.Python中定义的函数的关键字是(A)

A.def B.define C.function D.defunc

2.如果函数中没有return语句或return语句不带任何返回值,那么该函数()

A.没有返回值 B.返回值为None C.返回值为Nun D.返回值为Null

5.2 函数的参数传递

要点:函数可以定义可选参数,使用参数的位置或名称传递参数值,根据函数中变量的不同作用域有不同的函数返回值方式。

5.2.1可选参数和可变数量参数

在定义函数时,如果有些参数存在默认值,即部分参数不一定需要调用程序输入,可以在定义函数时直接为这些参数指定默认值。当函数被调用时,如果没有传入对应的参数值,则使用函数定义时的默认值替代。例如:

def dup(str, times=2):print(str*times)dup("knock~")
dup("knock~",4)

程序输出结果:

knock~knock~
knock~knock~knock~knock~

由于函数调用时需要按顺序输入参数,可选参数必须定义在非可选参数的后面,即dup()函数中带默认值得可选参数times必须定义在str参数后面。

==在函数定义时,也可以设计变量参数,通过在参数前增加(*)实现。==带有星号的可变参数只能出现在参数列表的最后。调用时,这些参数被当作元组类型传递到函数中,实例如下:

def vfunc(a,*b):print(type(b))for n in b:a += nreturn avfunc(1,2,3,4,5)

vfunc()函数定义了可变参数b,调用vfunc()函数时输入的(2,3,4,5)被当作元组传给b,与a累加后输出。

python的type函数有两个用法,当只有一个参数的时候,返回对象的类型。当有三个参数的时候返回一个类对象。

例:

#coding=utf-8
def Say(s="团结",n=2,m=1):for i in range(1,n+1):print(s*m)Say()
print
Say("合作!",3,4)
print
Say("合作!")

结果:

团结
团结
合作!合作!合作!合作!
合作!合作!合作!合作!
合作!合作!合作!合作!
合作!
合作!

可变数量参数:在python中,若形参可以接收不定个数的参数,该形参称为可变数数量参数。

​ 可变长度参数在定义函数时主要有两种形式:*parameter和**parameter

*parameter方式:无论调用时传递了多少实参,都放入元组。列如:

def all_1(*args):print('有',len(args),'个参数',':',args)all_1("团结合作","携手应对")
all_1("团结合作","携手应对",'守望相助')

形参args此时为一个元组,结果:

有 2 个参数 : ('团结合作', '携手应对')
有 3 个参数 : ('团结合作', '携手应对', '守望相助')

**parameter方式:自动将接受的参数转换为字典。

def all_2(**args):print(args)all_2(x="a",y="b",z=2)
all_2(m=3,n=4)

结果:

{'x': 'a', 'y': 'b', 'z': 2}
{'m': 3, 'n': 4}

思考5-2(课堂):

编写一个函数sum_all,参数可以接收任意个的数字,返回值为这些参数的和。

#coding=utf-8
# 法一
def sum_all(a,*b):for i in b:a = a+i     # 也可写成 a += ireturn aprint(sum_all(1,2,3))
print(sum_all(1,2,3,4,5,6))# 法2
def sum_all(*a):n=0for i in a:n=n+ireturn nprint(sum_all(1,2,3))
print(sum_all(1,2,3,4,5,6))

结果:

6
21

5.2.2 参数的位置和名称传递

函数调用时,实参默认采用按照位置顺序的方式传递给函数,但当参数很多时,这种调用参数的方式可读性较差。

假设func()函数有6个参数,它的定义如下,其中参数分别表示两组三维坐标值。

func(x1,y1,z1,x2,y2,z2)return
#实际调用如下:
result =func(1,2,3,4,5,6,)

如果仅看实际调用而不看函数定义,很难理解这些输入参数的含义。在规模稍大的程序中,函数定义可能在函数库中,也可能与调用相距很远,带来的可读性较差。

为了解决上述问题,Python提供了按照形参名称输入实参的方式,此时函数调用如下:

result = func(x2=4,y2=5,z2=6,x1=1,y1=2,z1=3)

由于调用函数时指定了参数名称,所以参数之间的顺序可以任意调整。

例:

def SayHello(s,n):for i in range(1,n+1):print(s)SayHello("Hello!",3)      #位置参数
SayHello(3,"Hello!")      #error
SayHello(n=3,s="Hello!")  #关键参数

位置参数:”Hello!“对应于s;3对应于n

Error: "Hello!"对应于n,则下面中的n+1无法对应从而出错

关键参数:把形参名直接赋值。

实参有两种类型:位置参数和关键参数,即函数实参是作为位置参数和关键参数被传递的。

当使用位置参数时,实参必须和形参在顺序、个数和类型上一一匹配。

传递参数时的序列解包

序列解包是指实参,同样也有*和**两种形式。

方式1:传递参数时,可以通过在实参序列前加*将其解包,然后传递给多个单变量形参。

def demo(a,b,c):print(a+b+c)seq = [1,2,3]
tup = (1,2,3)
dic = {1:'a',2:'b',3:'c'}
Set = {1,2,3}demo(*seq)    #把 seq= [1,2,3]打散掉变为 a=1,b=2,c=3
demo(*tup)
demo(*dic)    # 把字典的键传过去
demo(*dic.values())  # a='a',b='b',c='c' 字典打散掉
demo(*Set)

结果:

6
6
6
abc
6

方式2:如果函数实参是字典,可以在前面加**进行解包,等价于关键参数。

def demo(a,b,c):print(a+b+c)dic = {'a':1,'b':2,'c':3}
demo(**dic)      #传递字典的值
demo(a=1,b=2,c=3)   # 关键参数
demo(*dic.values())  #传递字典的值
demo(*dic)    #字典的键

结果:

6
6
6
abc

总结:

实参

  • 位置参数
  • 关键参数 f(b=4,a=3)
  • 实参为序列、字典,传递参数时解包

形参

  • 默认参数def函数名(形参=值)

  • 可变参数def函数名(*形参)

  • 引用字典def函数名(**形参)

全局变量:global

5.2.3 函数的返回值

函数返回值:

  • 函数不一定要有return语句
  • 即使函数无返回值,依旧可以获得返回值None
  • return不带任何参数时,也返回None
  • 如果函数有返回值,使用return来返回值
  • 执行return语句意味着函数的终止
  • Python的return语句可以返回多个值

例:

#eg5_1sort.py
def sort(number1,number2):if number1<number2:return number1,number2else:return number2,number1n1,n2 = sort(3,2)
print("n1 is",n1)
print("n2 is",n2)

程序结果:

n1 is 2
n2 is 3

return语句用来退出函数并将程序返回到函数被调用的位置继续执行。return语句可以同时将0个、1个或多个函数运算后的结果返回给函数被调用处的变量。

例如:

def func(a, b):return a*bs = func("knock~",2)
print(s)

程序结果:

knock~knock~

函数也可以用return返回多个值,多个值以元组类型保存,如:

def func(a,b):return b,as = func("knock",2)
print(s,type(s))

程序结果:

(2, 'knock') <class 'tuple'>

若没有return,例如:

def sum(number1,number2):total = number1+number2print(sum(1,2))

程序结果:

None

加上return,则:

def sum(number1,number2):total = number1+number2return totalprint(sum(1,2))

程序结果:

3

def circle(r):if r<=0:print("要求输入整数!")return                  # 类似return None ,其中None可省略area = 3.14*r*rperimeter=2*3.14*rreturn area, perimeter    #返回一个元组r=-3
re=circle(r)
if re!=None:print("半径为",r,"的圆面积为:",re[0])print("半径为",r,"的圆周长为:",re[1])r=3
re=circle(r)
if re!=None:print("\n半径为",r,"的圆面积为:",re[0])print("半径为",r,"的圆周长为:",re[1])

程序结果:

要求输入整数!半径为 3 的圆面积为: 28.259999999999998
半径为 3 的圆周长为: 18.84

5.2.4 函数对变量的作用

一个程序中的变量包括两类:全局变量和局部变量。

全局变量:在函数之外定义的变量,一般没有缩进,在程序执行全过程有效。

局部变量:在函数内部使用的变量,仅在函数内部有效,当函数退出时变量将不存在。

以下例子说明,当函数执行完退出后,其内部变量将被释放:

n = 1       #n 是全局变量
def func(a, b):c = a * b       # c是局部变量,a和b作为函数参数也是局部变量return cs = func("knock~",2)
print(c)

程序结果:

NameError: name 'c' is not defined

若函数使用全局变量时:

>>> n = 1
>>> def func(a,b):n = a * breturn n>>> s =func("knock~",2)
>>> print(s,n)   #测试一下n值是否改变
('knock~knock~', 1)

函数func()内部使用了变量n,并且将变量参数b赋值给变量n,为何 n值没有改变?因为函数func()有自己的内存空间,它将n=b语句理解为生成一个局部变量n,并将参数b赋值给它,此时func()函数没有将n当作全局变量。所以,函数退出后,局部变量n被释放,全局变量n的值没有改变。

如果希望让func()函数将n当作全局变量,需要在变量n使用前用global显示声明该变量为全局变量,代码如下:

>>> n = 1  # n是全局变量
>>> def func(a,b):global nn = b #将局部变量b赋值给全局变量nreturn a*b>>> s = func("knock~",2)
>>> print(s,n) #测试一下n值是否改变
knock~knock~ 2

倘若全局变量不是整数n,而是列表类型ls则:

>>>ls = []    #ls是全局列表变量
>>>def func(a,b):ls.append(b)   #将局部变量b增加到全局列表变量ls中return a*b>>>s = func("knock~",2)
>>>print(s,ls)   # 测试一下ls值是否改变
knock~knock~ [2]

列表全局变量在函数func()调用后竟然发生了改变,与整数变量n不同。

列表等组合数据类型由于操作多个数据,所以它们在使用中有创建和引用的分别。当列表变量被方括号([],无论是否为空)赋值时,这个列表才被真是穿件,否则只是对之前创建列表的一次引用。

上述代码func()函数的ls.append(b)语句执行时需要一个真是创建过的列表,此时func()函数专属的内存空间中没有已经创建过且名称为ls的列表,因此,func()函数进一步去寻找全局内存空间,自动关联全局ls列表,并修改其内容。当func()函数退出后,全局列表ls中的内容被修改。简单地说,对于列表类型,函数可以直接使用全局列表而不需要采用global进行声明。

但如果func()函数内部存在一个真实创建过且名称为ls的列表,则func()函数将操作该列表而不会修改全局变量,例如:

>>>ls = []  #ls是全局列表变量
>>>def func(a,b):ls = []   #创建了名称为ls的局部列表变量ls.append(b)    #将局部变量b增加到局部列表变量ls中return a*b>>>s = func("knock~",2)
>>>print(s,ls)
knock~knock~ []

例:

def f(s):s[0]="hello"a=[1,2,3,4]
f(a)      # s=a 指向位置是一样 s=[1,2,3,4] 改变s[0]即列表里第一个数字1变为”hello“
print(a)  # f()函数内部并没有存在一个真实创建国的列表b=[1,2,3,4]
f(b[:])  # s=b[:] 拷贝 指向是不同的位置,不同地址空间 原本的b并未改变 此处创建了b[:]这个列表
print(b)  #

结果:

['hello', 2, 3, 4]
[1, 2, 3, 4]

总结 python函数对变量的作用遵守如下原则

(1)简单数据类型变量无论是否与全局变量重名,仅在函数内部创建和使用,函数退出后变量被释放,如有全局同名变量,其值不变。

(2)简单数据类型变量在用global保留字声明后,作为全局变量使用,函数退出后该变量保留且值被函数改变。

(3)对于组合数据类型的全局变量,如果在函数内部没有被真实创建的同名变量,则函数内部可以直接使用并修改全局变量的值。

(4)如果函数内部真实创建了组合数据类型变量,无论是否有 同名全局变量,函数仅对局部变量进行操作,函数退出后局部变量被释放,全局变量值不变。


拓展:指针和引用

指针:保存内存地址的变量,一般出现在比较底层的 程序设计语言中,如C语言。

引用:某一变量的别名,用这个名字可以对变量进行操作,如python列表类型的引用。

两者主要区别:指针直接指向内存地址,说明对象已经生成,而引用只是别名,需要真实创建对象才能操作对象。由于列表类型在Python中十分常用,要格外注意该类型真实创建和引用的区别。


补充


生成器函数设计要点:

  • 包含yield语句的函数可以用来创建生成器对象,这样的函数也称为生成器函数

  • yield语句与return语句的作用相似,都是用来从函数中返回值

  • 与return语句不同的是,return语句一旦执行会立刻结束函数的运行,而每次遇到yield时函数会暂停并保存当前所有的运行信息,返回yield的值,并在下一次执行next()方法时从当前位置继续运行。

def f():for i in 'abcdefg':yield i #使用yield表达式创建生成器x = f()     # x其实是个生成器,是个对象,里头的值abcdefg都可访问到 从第一个字母i=a开始然后中断
next(x)     # 使用next()获取生成器对象中的元素 再次开始 则 x=a, i=b然后中断
next(x)     # 再一次开始 x=b  i=c然后中断
for item in x:    #输出x中的剩余元素print(item,end='')

结果:

cdefg

end 为末尾传递一个空字符串,这样print函数不会再字符串末尾添加一个换行符,而是添加一个空字符串,其实这也是一个语法要求,表示这个语句没有结束。

# 问题解决:使用生成器模拟了生成等差数列
def count(start,step):
# start 为等差数列的起始值,step为步长num = start while True:yield num     #返回一个数,暂停执行,等待下一次索要数据num += stepx = count(3,5)
for i in range(10):print(next(x),end="")
print()     # 即为一个\n 换行
for i in range(10):print(next(x),end="")

结果:

3 8 13 18 23 28 33 38 43 48
53 58 63 68 73 78 83 88 93 98

产生斐波那契数列

# 产生斐波那契数列
def f():a,b =1,1while True:yield a     # 暂停执行,需要时再产生新元素a,b = b, a+b  # 继续生成新元素x = f()     # 创建生成器对象
for i in range(10):  # 斐波那契数列前10个元素print(next(x))    #使用next()获取生成器对象中的元素

结果:

1
1
2
3
5
8
13
21
34
55

思考与练习:

5.5 如何定义带有可选参数的函数?

答:在函数定义时,直接为可选参数指定默认值。可选参数必须定义在非可选参数后面,可选参数可以有多个。

5.6 如何定义带有可变数量参数的函数?

答:在函数定义时,可变参数通过在参数前增加星号(*)实现。可变数量参数只能在参数列表最后,即它只能有一个。

5.7 假如return语句同时返回3个值,返回值是什么数据类型?

答: 返回值是元组类型

5.8 参数的位置传递和名称传递各有什么优缺点?

答:位置传递:支持可变数量参数,但容易忘记实参的含义。

​ 名称传递:不易忘记实参的含义,但不支持可变数量参数。

5.9 在函数中操作全局列表类型变量时需要注意什么问题?

答:如果函数里没有创建同名变量,则可以直接使用,不需global声明。

5.3 模块3:datetime库的使用

要点:Python时间处理的标准函数库datetime提供了一批显示日期和时间的格式化方法。

5.3.1 datetime库概述

datetime库:Python提供了一个处理时间的标准库datetime,它提供了一系列由简单到复杂的时间处理方法。datetime库可以从系统中获得时间,并以用户选择的格式输出。

datetime库以格林威治时间为基础,每天由3600X24秒精确定义。该库包括两个常量:datetime.MINYEAR与datetime.MAXYEAR,分别表示datetime所能表示的最小、最大年份,值分别为1与9999.

datetime库以类的方式提供多种日期和时间表达方式。

(1)datetime.date:日期表示类,可以表示年、月、日等

(2)datetime.time:时间表示类,可以表示小时、分钟、秒、毫秒等。

(3)datetime.datetime:日期和时间表示的类,功能覆盖date和time类。

(4)datetime.timedelta:与时间间隔有关的类。

(5)datetime.tzinfo:与时区有关的信息表示类。

由于datetime.datetime类表达形式最为丰富,这里主要介绍这个类的使用。使用datetime类需要用import保留字,引用datetime类的方式如下:

from datetime import datetime

5.3.2 datetime库解析

datetime类(datetime.datetime类,以下简称为datetime类)的使用方式是首先创建一个datetime对象,让后通过对象的方法和属性显示时间。创建datetime对象有3种方法:datetime.now()、datetime.utcnow()和datetime.datetime().

1.使用datetime.now()获得当前日期和时间对象,使用方法如下

datetime.now()

作用:返回一个datetime类型,表示当前的日期和时间,精确到微秒。

参数:无

调用该函数,执行结果如下:

>>> from datetime import datetime
>>> today = datetime.now()
>>> today
datetime.datetime(2020, 5, 23, 23, 11, 10, 668633)

2.使用datetime.utcow()获得当前日期和时间对应的UTC(世界标准时间)时间对象,使用方法如下:

datetime.utnow()

作用:返回一个datetime类型,表示当前日期和时间的UTC表示,精确到微秒。

参数:无

调用该函数,执行结果如下:

>>> today = datetime.utcnow()
>>> today
datetime.datetime(2020, 5, 23, 15, 17, 25, 355724)

3.datetime.now()和datetime.utcnow()都返回一个datetime类型的对象,也可以直接使用datetime()构造一个日期和时间对象,使用方法如下:

datetime(year,month,day,hour=0,minute=0,second=0,microsecond=0)

作用:返回一个datetime类型,表示指定的日期和时间,可以精确到微秒。

参数如下:

year: 指定的年份,MINYEAR<= year<=MAXYEAR

month:指定的月份,1<= day <= 12

5.6 函数的递归

要点:函数定义中调用函数自身的方式形成递归

5.6.1 递归的定义

函数作为一种代码封装,可以被其他程序调用,当然也可以被函数内部代码调用。

递归:函数定义中调用自身的方式称为递归。

数学上经典的递归例子叫阶乘:

​ n ! = n ( n − 1 ) ( n − 2 ) . . . ( 1 ) n!=n(n-1)(n-2)...(1) n!=n(n−1)(n−2)...(1)

观察 5 ! 5! 5!的计算,如果去掉了5就剩下计算 4 ! 4! 4! ,推广来看, n ! = n ( n − 1 ) ! n!=n(n-1)! n!=n(n−1)! 。实际上这个关系给出了另一种表达阶乘的方式:
KaTeX parse error: Undefined control sequence: \mbox at position 22: …egin{cases} 1 &\̲m̲b̲o̲x̲ ̲n=0\\ n(n-1)! &…
这个定义说明0的阶乘按定义是1,其他数字的阶乘定义为这个数字乘以比这个数字个数字小1数的阶乘。递归不是循环,因为每次递归都会计算比它更小数的阶乘,知道 0 ! 0! 0!。 0 ! 0! 0!是已知的值,被称为递归的基例。当递归到底了,就需要一个能直接算出值得表达式。

递归的两个关键特征

(1)存在一个或多个基例,基例不需要再次递归,它是确定的表达式。

(2)所有递归链要以一个或多个基例结尾。


拓展:数学归纳法

数学归纳法和递归都利用了递推原理,本质是相同的。在证明一个与自然数相关的命题 P ( n ) P(n) P(n)时,数学归纳法采用如下步骤:

(1)证明当 n n n取第一个值 n 0 n_0 n0​时命题成立。

(2)假设当 n k n_k nk​( k ≥ 0 , k k\geq0,k k≥0,k为自然数)时命题成立,证明当 n = n k + 1 n=n_{k+1} n=nk+1​时命题也成立。

综合(1)和(2),对一切自然数 n ( n ≥ n 0 ) n(n\geq n_0) n(n≥n0​),命题 P ( n ) P(n) P(n)都成立。


5.6.2 递归的使用方法

例:阶乘的计算

根据用户输入的整数 n n n,计算并输出 n n n的阶乘值。

def fact(n):if n==0:            # 通过使用if语句给出了n为0时的基例,当n==0时函数不再递归,返回数值1return 1else:                   # 如果n!=0,则通过递归返回n与n-1阶的乘积return n * fact(n-1)      #fact()函数在其定义内部引用了自身,形成了递归过程
num = eval(input("请输入一个整数:"))
print(fact(abs(int(num))))    #由于负数和小数通过减1无法达到递归的基例(n==0),通过abs()和int()函数将用户输入转变成非负整数

程序结果:

请输入一个整数:10
3628800

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KbnAiNLx-1593708789911)(/Users/eve/Desktop/PYTHON/SchoolCourse/第5章函数和代码复用/Picture/屏幕快照 2020-06-02 22.07.26.png)]

上图为计算5!的递归调用过程,每次函数调用时,函数参数的副本会临时存储,递归中各函数再运算自己的参数,相互没有影响。当基例结束运算并返回值时,各函数逐层结束运算,向调用者返回计算结果。

使用递归一定要注意基例的构建,否则递归无法返回将会报错。

[例]字符串反转

对于用户输入的字符串s,输入反转后的字符串。

解决这个问题的基本思想是把字符串看作一个递归对象。长字符串由较短字符串组成,每个小字符串也是一个对象。假如把字符串看成仅由两部分组成:首字符和剩余字符串。如果将剩余字符串与首字符交换,就完成了反转整个字符串,代码如下:

def reverse(s):if s == "":   # reverse()超过递归深度是因为没有设计基例。字符串反转中的递归调用总是是使用比之前更短的字符串,因此,可以把基例设计为字符串的最短形式,即空字符串return s else:return reverse(s[1:])+s[0]    # s=abc revrse('bc')+'a' reverse('c')+'b' s[0]是首字符,s[1:]是剩余字符串,将它们反向链接str = input("请输入一个字符串:")
print(reverse(str))

reverse()函数没有基例,递归层数超过了系统的最大递归深度。默认情况下,当递归调用到1000层,Python解释器将终止程序。递归深度是为了防止无限递归错误而设计的,当用户编写的正确递归程序超过1000层时,可以通过如下代码设定:

import sys
sys.setrecursionlimit(2000)  #2000是新的递归层数

例eg5.20:

用递归方法来实现二分(折半)查找方法。列表中元素是按升序排列,输入一个数,用折半查找方法,判断该数是否在列表中

def search(seq,number,down,up):    # 列,元,下,上if down == up:if number == seq[down]:   print(down)else:print("没有这个数")returnelse:middle=(down+up)//2       #取中间值 向下取整if number>seq[middle]:    #与中间值对比,是在前一部分还在后面一部分return search(seq,number,middle+1,up)else:return search(seq,number,down,middle)seq=[4,8,34.67,95,100,123]
search(seq,34,0,len(seq)-1)

思考5-3:一天猴子摘了若干桃子,每天吃现有桃子数的一半多1个,第7天早上只剩下1个桃子,问猴子一共摘了多少桃子?试用迭代和递归两种方法实现:

解:

第五章 函数和代码复用相关推荐

  1. 第 5 章 函数和代码复用

    整理的文章内容主要来源为高教版<计算机等级考试二级 Python>教程视频讲义,并且更正了原讲义中的错误的地方. 专栏文章索引如下: 考试大纲 第 1 章 程序设计基本方法 第 2 章 P ...

  2. python函数是一段具有特定功能的语句组_Python学习笔记(五)函数和代码复用

    本文将为您描述Python学习笔记(五)函数和代码复用,具体完成步骤: 函数能提高应用的模块性,和代码的重复利用率.在很多高级语言中,都可以使用函数实现多种功能.在之前的学习中,相信你已经知道Pyth ...

  3. 《Go语言圣经》学习笔记 第五章函数

    <Go语言圣经>学习笔记 第五章 函数 目录 函数声明 递归 多返回值 匿名函数 可变参数 Deferred函数 Panic异常 Recover捕获异常 注:学习<Go语言圣经> ...

  4. python中组合数据类型、函数和代码复用的难点_Python电子教案5-2 函数和代码复用...

    <Python电子教案5-2 函数和代码复用>由会员分享,可在线阅读,更多相关<Python电子教案5-2 函数和代码复用(56页珍藏版)>请在人人文库网上搜索. 1.七段数码 ...

  5. 【python第五章——函数】

    python第五章--函数 第五章--函数 5.1函数概述 5.2 函数进阶 下面部分之后再写 5.3函数习题(一些小例子) 第五章--函数 5.1函数概述 快速入门: #定义函数 def func( ...

  6. stata:stata软件教程(人大十八讲)(5) 第五章 函数与运算符

    第五章 函数与运算符 5.1 运算符 exp 5.1.1 代数运算 5.1.2 字符运算 5.1.3 关系运算 5.1.4 逻辑运算 5.2 函数概览 function 5.3 数学函数 5.3.1 ...

  7. 测验5: 函数和代码复用 (第5周)

    测验5: 函数和代码复用 (第5周) 文章目录 测验5: 函数和代码复用 (第5周) 单选题 程序题 这是python123官网上联合MOOC的Python程序设计(第10期)答案 单选题 第三题补充 ...

  8. 软件构造 第五章第三节 可复用的设计模式

    第五章第三节 可复用的设计模式 Structural patterns 1.适配器模式(Adapter) 问题描述:其中LegacyRectangle是已有的类(需要传入矩形的一个顶点.长和宽),但是 ...

  9. Python基础(三)_函数和代码复用

    三:函数和代码复用 (一)函数的基本使用 1.函数的定义 函数是一段具有特定功能的.可重用的语句组,用函数名来表示并通过函数名进行功能调用.函数也可以看作是一段具有名字的子程序,可以在需要的地方调用执 ...

最新文章

  1. 编写fun函数判断字符串尾部的*号,若多于指定数量,则删除多余的;否则,不做操作
  2. 圣何塞与 Microsoft 宣布该市为超过 5,000 名市府公务员选择 Office 365、Windows Azure 和 StorSimple...
  3. Google Map App 问题集锦
  4. 认清楚服务器的真正身份--深入ARP工作原理
  5. 常用的Net Command
  6. matlab-游标及查询
  7. 【NLP】看不懂bert没关系,用起来so easy!
  8. centos web 访问mysql_Centos7安装Web服务器--Mysql5.7.12安装
  9. Python基础学习篇-2-数值运算和字符串
  10. ib课程计算机科学教材,热门课程:IB计算机科学的评估方式
  11. 查询当天交易总额最大的用户信息_场内场外交易
  12. 小米MIUI光标适配问题
  13. UVA 1329 Corporative Network(并查集:路径压缩)
  14. 限制mysql资源使用率_MySQL--限制用户使用资源
  15. USBKEY全解析---概要介绍
  16. 2020计算机专业保研夏令营面经:中科院计算所网数机试题目
  17. Android 模拟器 连接局域网
  18. docker日志显示时间时区错误,时区UST问题/群晖docker日志时间不正确 寻找解答过程
  19. win10Edge浏览器或第三方浏览器网页字体模糊解决方法
  20. 苹果CMS内容管理系统 - 苹果CMS官方网站

热门文章

  1. win10系统如何在地图定位服务器地址,win10地图服务器地址
  2. MATPLOTLIB 绘图色块
  3. Python加密解密程序
  4. 嵌入式Linux的基本命令
  5. 幸福的种子—亲子共读图画书
  6. 弘辽科技:拼多多怎么通过关键词提升销量?
  7. iOS compare字符串的比较
  8. js实现按返回键,不返回上一个页面
  9. jdi屏幕斜纹_jdi和amoled哪个屏好_amoled和jdi对比评测
  10. 罗侍田:DJYOS命名由来