第18课 自定义函数的创建与调用
学习要点
(1)能够创建简单的自定义函数。
(2)掌握自定义函数的调用方法。
(3)理解函数的返回值。
(4)理解变量作用域、全局变量与局部变量。
对标内容
(1)能够创建简单的自定义函数,掌握自定义函数的调用。
(2)理解函数的返回值、变量作用域等概念。
函数的返回值
知识点详解
函数的返回值
函数不是直接显示输出的,它会处理一些数据并返回一个或一组值。
函数用return 语句将值返回调用函数的代码行,返回值能将程序大部分繁重的工作移交到函数中去完成,从而简化主程序。
下面是一个简单的程序,用于接收姓氏和名字,然后返回完整的人名信息。
def name(first_name,last_name):
full_name=first_name+" "+last_name
return full_name
print(name("zhangsan","san"))
运行结果:
zhangsan san
函数可以返问任何类型的值,包括字典、列表这样较复杂的数据结构。还是上面的例子,这次返回一个表示人的字典。
def name(first_name,last_name):
full_name={"first":first_name,"last":last_name}
return full_name
print(name("zhangsan","lisi"))
运行结果:
{'first': 'zhangsan', 'last': 'lisi'}
函数传递列表
传递列表在函数中很有用,列表中包含数字、名字甚至更复杂的对象,如下例所示。
def f(names):
for i in names:
print("Hello"+" "+i+"!!")
f(["zhangsan","lisi","wangwu"])
运行结果:
Hello zhangsan!!
Hello lisi!!
Hello wangwu!!
思考
(1)对于带返回值的函数,输入并运行以下代码。
def fact(n):
factorial=1
for counter in range(1,n+1):
factorial*=counter
return factorial
n=int(input('calculate n! Enter n=?'))
print(n,'!=',fact(n))
(2)对于带默认值的函数,输入并运行以下代码。
def rt1(a=3):
for n in range(a):
for m in range(n+1):
print('*',end='')
print()
rt1()
rt1(5)
易错点
(1)比对带返回值与不带返回值的自定义函数的差别,理解它们的含义。
(2)在函数中用return语句将值返回调用函数的代码行。
模拟考题
考题1 单选题
关于以下程序,下列表述中错误的一项是( )。
def demo(n):
s=1
for i in range(1,n):
s*=i
return s
A.demo(n)函数的功能是求n的阶乘 B. s是局部变量
C. n是形参 D. range())函数是Python内置函数
答案:A
解析∶demo(n)函数的功能是求阶乘,range(1,n)是从1循环到n-1。
考题2 单选题
运行以下程序,输出结果正确的是( )。
def demo(x):
return x*2;
print(demo(demo(demo(1)))
A. 1 B. 2 C. 4 D. 8
答案:D
解析∶函数被调用3次,1乘以2,再乘以2,再乘以2,结果为8。
考题3 判断题
函数体中必须包含 return 语句。( )
答案:错误
解析∶自定义函数可以没有返回值。
全局变量和局部变量
知识点详解
一般定义在程序最开始处的变量称为全局变量,而在函数中定义的变量称为局部变量。
可以简单理解为,无缩进的为全局变量,有缩进的是局部变量。
全局变量的作用域是整个程序,而局部变量的作用域是函数内部。
当程序运行时,首先会找程序内部有没有局部变量,如果有,则调用;如果没有,才去调用全局变量。
name='zhang' #全局变量
def f():
name="li" #局部变量
print(name)
f()
print(name) #打印全局变量name的值
运行结果:
li
zhang
调用f()函数,程序会先在函数内部找有没有name 这个变量,如果有,就会使用该name的值;而如果没有定义局部变量name,函数再去找全局变量name。
可以通过global关键字,通过局部变量修改全局变量的值,如下例所示。
name="zhang" #定义全局变量
def f():
global name #在函数内部,通过global关键字,通过局部变量修改全局变量的值
name="li"
print(name)
f() #打印局部变量name的值
print(name) #打印全局变量name的值
运行结果:
li
li
在运行结果中可以明显看出,使用global关键字后,在定义局部变量的同时也修改了全局变量的值。
global与nonlocal的区别:
global关键字用来在定义局部变量的同时,修改全局变量的值;
nonlocal关键字用来在函数或局部作用域使用外层(非全局)变量。
def add():
count=1
def fun():
nonlocal count
print(count)
count+=2
return fun
a = add()
a()
a()
运行结果:
1
3
思考
(1)对于局部变量作用域,输入下列代码,并运行试试。
def f1():
x=5
y=6
print(x+y)
def f2(): #改为(x)
y=1
print(x+y) #出错!不能引用f1()中的x
f1()
f2()
调用f2()时出错了,处理办法有以下两种。
方法1:将 “def f2():” 改为 “def f2(x):”
方法2:将“x=5”从f1()中移出来,使x变为全局变量。
(2)如果在函数中定义的局部变量与全局变量同名,则调用函数时,局部变量屏蔽全局变量。输入下列代码,并运行试试。
x='outside'
y='global'
def f():
x='inside'
print(x)
print(y)
f()
print(x)
运行结果:
inside
global
outside
易错点
(1)理解global与nonlocal关键字的区别和它们各自的用法。
(2)如果在函数中定义的局部变量与全局变量同名,则调用函数时,局部变量屏蔽全局变量。
模拟考题
考题1 单选题
运行以下程序,输出的结果是()。
x=1
def demo():
global x
x=2
print(x)
demo()
print(x)
A. 1 B. 2 C. 1 D. 2
1 1 2 2
答案:D
解析:Python中定义函数时,若想在函数内部对函数外的变量进行操作,就需要在函数内部声明其为global,以改变它的值。
考题2 单选题
运行以下代码,正确的结果是()。
def f(s):
t=0
max=0
for i in s:
if i>"0" and i<="9":
t=t+1
else:
if t>max:
max=t
t=0
print(max)
list="123ab45cd6d"
f(list)
A. 0 B. 1 C. 2 D. 3
答案:D
解析:本段代码中,函数f(s)的作用是求最长的连续数字字符串的长度。
考题3 判断题
调用嵌套函数outer(),两次输出变量x的值是不一样的。()
def outer():
x="local"
def inner():
x="nonlocal"
print("inner:",x)
inner()
print("outter:",x)
答案:正确
解析:在嵌袭函数中,其内部与外部相同名称的变量是互不影响的,所以两次输出变量的值是不一样的。
为函数的参数和返回值指定类型
知识点详解
Python是动态类型语言,新建变量时不需要声明与指定类型,自定义函数时也是如此。
但是,Python 3.5之后的版本就新增了对函数参数和返回值的类型指定和检查,新建变量时也可以指定类型。
例如下面这个函数,指定了输入参数a的类型为int,而b的类型为str,并且返回值的类型为str。
可以看到,调用此函数,最终返回了一个字符串。
def f(a:int,b:str)-> str:
c=a*b
print(c)
return f
f(3, 'zhongguo!')
运行结果
zhongguo!zhongguo!zhongguo!
当我们调用这个函数时,如果参数a输入的是字符串,实际上运行不会报错,毕竟Python的本质还是动态类型语言。
def f(a:int,b:str)-> str:
print(a,b)
return 500
f('nihao','zhongguo!')
运行结果∶
nihao zhongguo!
易错点
(1)Python 3.5之后的版本新增了对函数参数和返回值的类型指定和检查,新建变量时也可以指定类型。
(2)如果参数a输入的类型不匹配,实际上运行时不会报错。
模拟考题
考题1 编程题
设计一个算法,根据邮件的重量和用户是否选择加急计算邮费。
计算规则∶重量在1000克以内(含1000克),基本邮费8元。超过1000 克的部分,每500克加收超重费4元,不足500克部分按500克计算。如果用户选择加急,多收5元。
根据上述计算规则,编写自定义函数完成程序功能,或补全代码。
描述∶根据邮件的重量和用户是否选择加急计算邮费。
函数名∶postage(w∶int,f∶str)->int
参数表∶w代表邮件的重量(整数)。f是表示是否加急的字符串,其中'y'和'n'分别表示加急和不加急。
返回值:返回邮费(整数)。
示例∶当 w=1200,f='y'时,返回17。
def postage(w:int,f:str)->int:
if f == 'y':
cost = ____①____
else:
cost = ____②____
if w > 1000:
cost += ____③____
if (w-1000)%500 > 0:
cost += 4
return cost
w = int(input('邮件的重量:'))
f = input('是否加急:')
print(postage(w, f))
评分标准:
① 5+8或等效答案(4分);
② 8或等效答案(4分)
③ (w-1000)//500*4或等效答案(4分)。
参数传递
在 python 中,类型属于对象,对象有不同类型的区分,变量是没有类型的:
a=[1,2,3]
a="Runoob"
以上代码中,[1,2,3] 是 List 类型,"Runoob" 是 String 类型,而变量 a 是没有类型,她仅仅是一个对象的引用(一个指针),可以是指向 List 类型对象,也可以是指向 String 类型对象。
可更改与不可更改对象
在 python 中,strings, tuples和 numbers 是不可更改的对象,而 list,dict 等则是可以修改的对象。
- 不可变类型:变量赋值 a=5 后再赋值 a=10,这里实际是新生成一个 int 值对象 10,再让 a 指向它,而 5 被丢弃,不是改变 a 的值,相当于新生成了 a。
- 可变类型:变量赋值 la=[1,2,3,4] 后再赋值 la[2]=5 则是将 list la 的第三个元素值更改,本身la没有动,只是其内部的一部分值被修改了。
python 函数的参数传递:
- 不可变类型:类似 C++ 的值传递,如整数、字符串、元组。如 fun(a),传递的只是 a 的值,没有影响 a 对象本身。如果在 fun(a) 内部修改 a 的值,则是新生成一个 a 的对象。
- 可变类型:类似 C++ 的引用传递,如 列表,字典。如 fun(la),则是将 la 真正的传过去,修改后 fun 外部的 la 也会受影响
python 中一切都是对象,严格意义我们不能说值传递还是引用传递,我们应该说传不可变对象和传可变对象。
python 传不可变对象实例
通过 id() 函数来查看内存地址变化:
def change(a):
print(id(a)) # 指向的是同一个对象
a=10
print(id(a)) # 一个新对象
a=1
print(id(a))
change(a)
以上实例输出结果为:
4379369136
4379369136
4379369424
可以看见在调用函数前后,形参和实参指向的是同一个对象(对象 id 相同),在函数内部修改形参后,形参指向的是不同的 id。
传可变对象实例
可变对象在函数里修改了参数,那么在调用这个函数的函数里,原始的参数也被改变了。例如:
# 可写函数说明
def changeme( mylist ):
"修改传入的列表"
mylist.append([1,2,3,4])
print ("函数内取值: ", mylist)
return
# 调用changeme函数
mylist = [10,20,30]
changeme( mylist )
print ("函数外取值: ", mylist)
传入函数的和在末尾添加新内容的对象用的是同一个引用。故输出结果如下:
函数内取值: [10, 20, 30, [1, 2, 3, 4]]
函数外取值: [10, 20, 30, [1, 2, 3, 4]]
可变对象在函数内部如果重新赋值,原始的参数不会被改变了。例如:
# 可写函数说明
def changeme( mylist ):
#重新赋值
mylist=[1,2,3,4]
print ("函数内取值: ", mylist)
return
# 调用changeme函数
mylist = [10,20,30]
changeme( mylist )
print ("函数外取值: ", mylist)
输出结果如下:
函数内取值: [1, 2, 3, 4]
函数外取值: [10, 20, 30]
易错点
(1)不可变对象类型参数,函数内部对参数进行修改,不会影响原实参。
(2)可变对象类型参数,函数内部对参数进行修改,会影响原实参。
(3)不可变和可变对象类型参数,函数内部如果使用”=“进行重新赋值,都不会影响原实参。
模拟考题
考题1 单选题
下列代码的输出结果是?( )
def func(a, b):
c=a**2+b
b=a
return c
a=10
b=2
c=func(b,a)+a
print(c,b)
A. 102 2 B. 24 10 C. 24 2 D. 102 10
答案:C
解析:b=2,属性不可变对象,函数内部修改,不会影响外部实参
考题2 单选题
运行以下代码,输出结果正确的是?( )
a=1
b=c=[]
def fun(a,c):
a=2
c.append(a)
fun(a,c)
print(a,b,c)
A. 2 [2] [2]
B. 1 [] [2]
C. 1 [2] [2]
D. 2 [] [2]
答案:C
解析:a为整数为不可变类型,函数内部修改不会影响外部变量,b=c=[]为列表为可变类型,函数内部修改会影响外部变量
综合训练
实现求和函数
编写一个函数mySum,求一个列表的和(不可以可以使用原生函数sum),参考代码如下:
def mySum(a):
s=0
for i in a:
s+=i
return s
b=[1,3,5,7,9]
c=mySum(b)
print(c)
运行结果:
25
实现求最小值函数
编写一个函数myMin,求一个列表的最小值(不可以可以使用原生函数min),参考代码如下:
def myMin(a):
minN=a[0]
for i in a:
if i<minN:
minN=i
return minN
b=[1,3,5,0,7,9]
c=myMin(b)
print(c)
运行结果:
0
函数单元测试题
模拟考题
一、单选题(共25题,每题2分,共50分)
1、有如下Python语句,执行该语句后,结果是?( )
f=lambda x:5
print(f(3))
A. 3
B. 没有输出
C. 5
D. None
2、执行如下Python代码后,结果是?( )
def inverse(s,n=0):
while s:
n = n * 10 + s % 10
s = s // 10
return n
print(inverse(456,123))
A. 654123
B. 123456
C. 123654
D. 654321
3、如下代码运行后下面选项中描述错误的是?( )
def pph(a,b):
c=a**2+b
b=a
return c
a=10
b=100
c=pph(a,b)+a
print(a,' ',b,' ',c)
A. 执行该函数后,变量a的值为10
B. 执行该函数后,变量b的值为100
C. 执行该函数后,变量c的值为200
D. 该函数名称为pph
4、有如下Python程序,执行该程序后,结果是?( )
x = 3
def calc():
x = 5
print(calc())
A. 3
B. 5
C. 无输出
D. None
5、关于以下程序,下列表述中错误的一项是?( )
c=1
def fun(n):
a=1
for b in range(1,n):
a*=b
return a
n=int(input('Enter n='))
print(fun(n),c)
A. c是全局变量,a是局部变量
B. n是形式参数,当n=5时,程序输出120 1
C. 程序实现求阶乘
D. range()函数是python内置函数
6、以下程序的运行结果是?( )
def f(x,y,z):
print(x,y,z)
f(z=3,x=2,y=1)
A. 3 2 1
B. 1 2 3
C. 2 1 3
D. 3 1 2
7、运行以下代码,正确的打印结果是?( )
def f(s):
t=0
max=0
for i in s:
if i>="0" and i<="9":
t=t+1
else:
if t>max:
max=t
t=0
print(max)
list="123ab45cd6d"
f(list)
A. 0
B. 1
C. 2
D. 3
8、在Python程序中,设已定义函数op,它有一个整型传值参数,一个字符串型传值参数。设x,y为整型变量,z为字符串型变量,则下列能调用该函数的正确语句是?( )
A. op
B. op(x,y,z)
C. op x,y
D. op(x+y,z)
9、运行以下程序输出的结果是?( )
x=1
def demo():
global x
x=2
print(x)
demo()
print(x)
A. 1 1
B. 2 1
C. 1 2
D. 2 2
10、 代码:
def sum(num1,num2):
num1=num1+1
num2=num2+2
result=num1+num2
return result
a=3
b=4
c=sum(a,b)
print("a=",a,"b=",b,"c=",c)
以下说法正确的是?( )
A. 程序的运行结果为:a= 3 b= 4 c= 10
B. 程序的运行结果为:a= 4 b= 6 c= 10
C. 程序的运行结果为:a= 4 b= 6 c= 7
D. 编译错误,程序无法运行
11、有如下Python程序,包含lambda函数,运行该程序后,输出的结果是?( )
g = lambda x,y:x*y
print(g(2,3))
A. 2
B. 3
C. 6
D. 8
12、运行下列程序,输出的结果是?( )
def dtox(x,base = 2):
s = []
while x>0:
s.append(x % base)
x = x // base
return s
print(dtox(11))
A. 报错
B. 1101
C. [1,1,0,1]
D. 1
13、运行下列程序,输出的结果是?( )
def nxs(x):
s = 0
while x:
s = s * 10 + x % 10
x //= 10
return s
print(nxs(106))
A. 106
B. 610
C. 160
D. 601
14、对于下面的函数:
def f(x,z,y=2):
print(x+y+z)
用f(1,2,3)和f(1,2)两个调用语句,运行结果分别是?( )
A. 都是5
B. 6和5
C. 都是6
D. 5和6
15、函数如下:
def showNumber(numbers):
for n in numbers:
print(n)
下面哪个选项在调用该函数时会报错?( )
A. showNumber([2,4,5])
B. showNumber('abcesf’)
C. showNumber(3.4)
D. showNumber((12,4,5))
16、 运行下列这段程序,正确的输出结果是?( )
LS = ["apple", "red", "orange"]
def funb(a):
LS.append(a)
funb("yellow")
print(LS)
A. ["apple","red","orange"]
B. ["apple","red","orange","yellow"]
C. []
D. ["yellow"]
17、关于函数,以下选项中描述错误的是?
A. 函数能完成特定的功能,对函数的使用不需要了解函数内部实现原理,只要了解函数的输入输出方式即可
B. 使用函数的主要目的是减低编程难度和代码重用
C. Python 使用 del 保留字定义一个函数
D. 函数是一段具有特定功能的、可重用的语句组
18、函数调用时通过“键-值”形式加以指定实际参数,即使用关键字参数,可以让函数更加清晰、容易使用,关于关键字参数的限制,以下表述中哪一个是错误的?
A. 关键字参数之间不存在先后顺序
B. 不得重复提供实际参数
C. 关键字参数可以位于位置参数之前
D. 关键字参数必须位于位置参数之后
19、以下函数要计算1到100自然数的和,则划线处应补充选项为? ( )
def su(n):
s =0
while n > 0:
—————————
—————————
return s
print(su(100))
A. n=n-1 s=s+n
B. n=n+1 s=s+n
C. s=s+n n=n-1
D. s=s+n n=n+1
20、以下关于Python中使用函数的描述,错误的是?( )
A. 程序里一定要有main函数
B. 使用函数前要先定义函数
C. 函数在被调用时才执行
D. 函数执行结束后,程序执行流程会自动返回到函数被调用的语句之后
21、运行下列程序,输出的结果是?( )
rst = lambda a,b=5:a*b
print(rst(5))
A. 5
B. 15
C. 25
D. 35
22、运行下列程序,输出的结果是?( )
def js(n):
s = 0
while n:
s = s * 10+n % 10
n //= 10
return s
print(js(20230110))
A. 20230110
B. 01103202
C. 2301102
D. 1103202
23、运行下列程序后,输出的结果是?( )
def jsh(n):
if n == 1:
return 1
else:
return n + jsh(n-1)
print(jsh(10))
A. 1
B. 35
C. 45
D. 55
24、下列关于函数返回值的描述中,正确的是?( )
A. Python函数的返回值使用很灵活,可以没有返回值,可以有一个或多个返回值
B. 函数定义中最多含有一个return语句
C. 在函数定义中使用return语句时,至少给一个返回值
D. 函数只能通过print语句和return语句给出运行结果
25、 关于下列代码的描述,错误的是?( )
def fact(n):
s=1
for i in range(1,n+1):
s*=i
return s
A. s不能在函数外使用
B. range()函数是Python内置函数
C. 如果n=4,返回的值为24
D. 代码中n是可选参数
二、判断题(共10题,每题2分,共20分)
26、执行以下代码:
def fun( name, age = 30 ):
print("Name:", name)
print("Age:", age)
return
fun( age=40, name="summy" )
fun( name="summy" )
程序输出的结果为:
Name: summy
Age: 40
Name: summy
Age: 40。
正确 错误
27、判断下列代码的正确与错误。
def f(a,b,c):
print(a+b,b,c-a)
f(8,b=2,6)
运行上述程序段,结果为10 2 -2。
正确 错误
28、下列程序段返回的值为“Hello!Python”。
lst="Hello!Python"
def f():
global lst
lst="Hello!"
return lst
f()
print(lst)
正确 错误
29、下图左右两段代码运行之后,打印出的结果不相同!请判断对吗?
根据题意,该题答案为:错误.
正确 错误
30、匿名函数L(x1,y1,x2,y2)的返回值是(x1-x2)**2 + (y1-y2)**2的计算结果。
L = lambda x1,y1,x2,y2:(x1-x2)**2 + (y1-y2)**2
正确 错误
31、在Python中调用函数的时候,必须将每个实参都关联到函数定义中的每一个形参,最简单的关联方式就是基于实参的顺序。但也可以通过关键字实参的“关键字-值”方式关联形参,这时就不必考虑函数调用过程中实参的顺序。( )
正确 错误
32、函数中return语句只能放在函数定义的最后。( )
正确 错误
33、Python中自定义函数的代码需要写在调用该函数之前。( )
正确 错误
34、自定义函数def块中的代码不是主程序的一部分,运行会跳过该段代码。( )
正确 错误
35、创建自定义函数时,不需要声明函数形参的类型,形参的类型由调用自定义函数时传递的实参类型确定。( )
正确 错误
答案
一、单选题 1、C 2、C 3、C 4、D 5、B 6、C 7、D 8、D 9、D 10、A
11、C
试题解析:g = lambda x,y:x*y,lambda函数返回参数x和y的积,因此选C。
12、C 13、D
14、B
试题解析:Python函数中位置参数和关键字参数。
15、C
试题解析:3.4是浮点数,不是序列对象
16、B
试题解析:该函数作用是添加列表元素。
17、C 18、C 19、C
20、A
试题解析:Python程序不必一定要有main函数
21、C
试题解析:rst(5)传入一个实参,a被赋值为5,b使用默认值5,因此结果是5*5=25,选C
22、D
试题解析:函数js的功能是计算n的逆序数,因此选D
23、D
试题解析:递归思想求解1到n之间和,因此答案选D
24、A
试题解析:Python函数的返回值使用很灵活,可以没有返回值,可以有一个或多个返回值,是正确的。
25、D
试题解析:代码中n是可选参数是错误的
二、判断题
26、错误 27、错误 28、错误 29、正确 30、正确 31、正确
32、错误
试题解析:return语句只要在函数体内都可以,一旦执行,退出函数体
33、正确
试题解析:Python中自定义函数的代码需要写在调用该函数之前
34、正确
试题解析:def块中的代码并不是主程序的一部分,程序运行时会跳过这一部分,从def块以外的第一行代码开始运行
35、正确
试题解析:创建自定义函数时,不需要声明函数形参的类型,形参的类型由调用自定义函数时传递的实参类型确定