第5章、函数和代码复用
1、函数的基本使用
函数是一段具有特定功能的,可重用的语句组,通过函数名来表示和调用。经过定义,一组语句等于一个函数,在需要使用这组语句的地方,直接调用函数名称即可。因此,函数包括两部分:函数的定义和函数的使用。
使用函数主要有两个目的:降低编程难度和增加代码复用。函数是一种功能抽象,利用它可以将一个复杂的大问题分解成一系列简单的小问题,分而治之,为每个小问题编写程序,通过函数封装,当各个小问题都解决了,大问题也就迎刃而解。函数可以在一个程序中多个位置使用,也可以用于多个程序,当需要修改代码时,只需要在函数中修改一次,所有调用位置的功能都更新了,这种代码复用降低了代码行数和代码维护难度。
- (1)、函数的定义
Python语言通过def关键字定义函数。
语法格式:
函数名可以是任何有效的 Python标识符,参数列表是调用该函数时传递给它的值,可以有零个、一个或多个,当传递多个参数时各参数由逗号分隔,当没有参数时也要保留圆括号。参数列表中参数是形式参数,简称为“形参”,相当于实际参数的一种符号表示或符号点位符。函数体是函数每次被调用时执行的代码,由一行或多行语句组成。如果需要返回值,使用保留字return和返回值列表。函数可以没有return语句,函数体结束后会将控制权返回给调用者。例:
#定义一个对整数n求阶乘的函数
def factorial(n):
result = 1
for i in range(1, n + 1):
result *= i
print(result)
factorial(5)
#运行结果:
#120
注意:当函数没有return时,仅表示执行一段代码功能。
- (2)、函数的使用
函数的定义也叫函数“声明”,定义后的函数不能直接运行,需要经过“调用”才能得到运行。
语法格式:
函数名(实际赋值参数列表)
每次使用函数可以提供不同参数作为输入,以实现对不同数据的处理;函数执行后,可以反馈相应的处理结果。函数的执行与黑盒类似,使用者不需要了解函数内部实现原理,只要了解函数的输入输出方式即可。
具体来说,函数的使用一共分为4个步骤:
① 函数的定义
使用def保留字将一段代码定义为函数,需要确定函数名、参数名、参数的个数,使用参数名称作形式参数(占位符)编写函数内部的功能代码。
② 函数的调用
通过函数名调用函数功能,对函数的各个参数赋予实际值,实际值可以是实际数据,也可以是在调用函数前已经定义过的变量。
③ 函数的执行
函数被调用后,使用实际参数(赋予形式参数的实际值)参与函数内部代码的运行,如果有结果则进行输出。
④ 函数的返回
函数执行结束后,根据return保留字的指示决定是否返回结果,如果返回结果,则结果将被放置到函数被调用的位置,函数使用完毕,程序继续运行。编程中大量使用函数已经成为一种编程范式,叫作函数式编程。函数式编程的主要思想是把程序过程尽量写成一系列函数调用,这能够使代码编写更简洁、更易于理解,是中小规模软件项目中最常用的编程方式。在Python中,函数也是有类型的,可以通过type()获得函数的类型。函数采用其定义的名字表达,具体为function类型,这是一种Python的内置类型。然而,如果调用函数,则类型为返回值的类型。
Python语言最小函数可以不表达任何功能,比如说:
2、函数的参数传递
- (1)、可选参数传递
函数的参数在定义时可以指定默认值,当函数被调用时,如果没有传入对应的参数时,则使用函数定义时的默认值替代。
语法格式:
def example_func(arg1, arg2, optional_arg1=0, optional_arg2='default'):
# 函数体
pass
在这个示例中,arg1 和 arg2 是非可选参数,它们在函数调用时必须提供。optional_arg1 和 optional_arg2 是可选参数,它们有默认值,可以选择性地提供。
需要注意的是,可选参数一般都放在非可选参数的后面,即定义函数时,先给出所有非可选参数,然后再分别列出每个可选参数及对应的默认值。
-
(2)、参数名称传递
函数调用时,默认采用按照位置顺序的方式传递函数,例如在函数multiply(x,y)中使用multiply(33,1)中第一个实参,默认赋值给形参x,第二个实参赋值给形参y。
Python语言同时支持函数按照参数名称方式传递参数。
语法格式:
函数名(参数名=实际值)
采用参数名称传递方式不需要保持参数传递的顺序,参数之间的顺序可以任意调整,只需要对每个必要参数赋予实际值即可,这种方式会显著增强程序的可读性。 -
(3)、函数的返回值
return语句用来结束函数并将程序返回到函数调用的位置继续执行。return语句可以出现在函数中的任何部分,同时可以将0个、1个或多个函数运算的结果返回给函数被调用处的变量。
注意:多个返回值问题
当return返回多个值时,这些值形成了一个元组数据类型,由小括号和逗号分隔,例如(a,b,c)。元组是Python内置的一种组合数据类型。函数可以没有return,此时函数并不返回值。
3、变量的作用域
- (1)、局部变量
局部变量指在函数内部定义的变量,仅在函数内部有效,当函数退出时变量将不再存在。
- (2)、全局变量
全局变量指在函数之外定义的变量,在程序执行全过程有效。全局变量在函数内部使用时,需要提前使用保留字global声明。
语法格式
global 全局变量
n = 2
def text(x):
global n
return n*x
print(text(8))
#运行结果为16
注意事项:全局变量声明
使用 global对全局变量声明时,该变量要与外部全局变量同名。
上例中,变量n是全局变量,在函数 text()中使用时需要在函数内部使用global声明,声明后即可使用。如果未使用保留字global声明,即使名称相同,也不是全局变量。
n = 2
def text(x):
n=5
return n*x
print(text(8))
#运行结果为40
使用global保留字声明变量的作用域是Python语言中少有需要再次声明的情形,由于作用域不同,这种声明不能省略。
4、代码的复用
函数是程序的一种基本抽象方式,它将一系列代码组织起来通过命名供其他程序使用。函数封装的直接好处是代码复用,任何其他代码只要输入参数即可调用函数,从而避免相同功能的代码在被调用处重复编写。代码复用有另一个好处,当更新函数功能时,所有被调用处的功能都被更新。
模块化设计是指函数的封装功能将程序划分成主程序、子程序和子程序间关系的表达。模块化设计是使用函数设计的思考方法,以功能块为基本单位,一般有两个基本要求:
紧耦合:尽可能合理划分功能块,功能块内部耦合紧密;
松耦合:模块间关系尽可能简单,功能块之间耦合度低。
函数是程序的一种基本抽象方式,它将一系列代码组织起来通过命名供其他程序使用。函数封装的直接好处是代码复用,任何其他代码只要输入参数即可调用函数,从而避免相同功能的代码在被调用处重复编写。代码复用有另一个好处,当更新函数功能时,所有被调用处的功能都被更新。
程序由一系列代码组成,如果代码是顺序但无组织的,不仅不利于阅读和理解,也很难进行升级和维护。当程序长度在百行以上,如果不划分模块,程序的可读性就已经很糟糕了。解决这一问题最好的方法是将一个程序分割成短小的程序段,每一段程序完成一个小的功能。使用函数对合理划分为功能模块,并基于模块设计程序是一种常用方法,被称为“模块化设计”。
模块化设计是指函数的封装功能将程序划分成主程序、子程序和子程序间关系的表达。模块化设计是使用函数设计的思考方法,以功能块为基本单位,一般有两个基本要求;
紧耦合:尽可能合理划分功能块,功能块内部耦合紧密;
松耦合:模块间关系尽可能简单,功能块之间耦合度低。
耦合性指程序结构中各模块之间相互关联的程序,它取决于各模块间接口的复杂程序和调用方式。耦合性是影响软件复杂程序和设计质量的一个重要因素。紧耦合指模块或系统间关系紧密,存在较多或复杂的相互调用。紧耦合的缺点在于更新一个模块可能导致其他模块变化,复用较困难。松耦合一般基于消息或协议实现,系统间交互简单。使用函数只是模块化设计的必要非充分条件,根据计算需求合理划分函数十分重要。一般来说,完成特定功能或被经常复用的一组语句应该采用函数来封装,并尽可能减少函数间参数和返回值的数量。
扩展:
Lambda函数是一种匿名函数,可以用于简化代码和编写一次性的函数。它的语法形式如下:
lambda arguments: expression
其中,arguments 是函数的参数列表,可以包括零个或多个参数。expression 是函数体,用于定义函数要执行的操作,并返回结果。
下面是一个使用Lambda函数的示例:
# 使用Lambda函数计算两个数的和
add = lambda x, y: x + y
result = add(3, 5)
print(result) # 输出:8
6、 选择题
- (1)用就定义函数的保留字是(B)
A.global
B.def
C.return
D.yield
- (2)以下关于Python函数的说法中正确的是()
A.可以用保留字作为函数的名字
B.函数内部可以通过关键字global 来声明全局变量c.
C.调用带有默认值参数的函数时,不能为默认值参数传递任何值,必须使用默认值
D.函数中没有return语句或者return语句不带任何返回值,那么该函数的返回值为True
- (3)程序最外层有一个变量a,定个一个函数,其中再次使用了变量,以下说法正确的是()
A.函数中将a声明为global,对a的操作与全局变量无关
B.函数中未将a声明为global,对a的操作与全局变量无关
C.函数中未将a声明为global,对 a的操作即为对全局变量a的操作
D.函数中将a声明为global,对 a的操作即为对全局变量a的操作,但函数返回时全局变量a被销毁。
- (4)给出以下代码,运行结果是()
def fun(a=1):
return a+1
func(func(func()))
A.1
B.2
C.3
D.4
- (5)给出以下代码,则print(type(func),type(func()))是()
def func():
print(“hello”)
A.<class ‘function’>,<class ‘function’>
B.<class ‘function’>,<class ‘str’>
C.<class’function’>,<class ‘NoneType’>
D.<class ‘str’>, <class ‘function’>
- (6)以下说法错误的是()
A. 函数定义不一定放在调用之前
B. 当代码中有main函数时,程序将从main开始执行
C. 可以在函数中定义函数
D. 语句a=func()中,func函数可以没有返回值
- (7)给出以下代码,程序的运行结果是()
s=’an apple a day’
def split(s)
return s.split(‘a’)
print(s.split())
A. [",’n’,’pple’,’d’,’y’]
B. [‘an’,‘apple’,’a’,’day’]
C. 在函数定义时报错
D. 在最后一行报错
- (8)定义函数如下,程序的运行结果是()
f=lambda x:x+1
f(f(1))
A.1
B.2
C.3
D.会报错
- (9)以下说法正确的是()
A. 函数内部的语句不会改变任何非全局变量的值
B. 任何传入函数的参数都会以副本的形式存在于函数中
C. 在函数内定义的子函数只能在函数内调用
D. 每个函数必须有至少一个 return语句
- (10)以下函数的运行结果是()
def func(ls=[):
ls.append(1)
王
10)以下函数的运行结果是()
def func(ls=[]):
ls.append(1)
return ls
a=func()
b=func()
print(a,b)
A.[1][1]
B. [1][1,1]
C. [1,1][1]
D. [1,1][1,1]
在第一次调用 func 函数时,没有传递任何参数,因此默认参数 ls 被赋值为空列表 []。然后 1 被添加到列表 ls 中,使其变为 [1]。函数返回了 [1],并将其赋值给变量 a。
在第二次调用 func 函数时,同样没有传递任何参数,所以默认参数 ls 仍然指向先前创建的列表 [1]。在这次调用中,又将 1 添加到列表 ls 中,使其变为 [1, 1]。函数返回了 [1, 1],并将其赋值给变量 b。
因此,由于默认参数 ls 在每次函数调用时重复使用,且列表被修改,所以 a 和 b 最终都是指向同一个值为 [1, 1] 的列表。
1B
2B
3B
4D
5C
6B
7B
8C
9C
10D
7、 编程题
- (1)、实现 isNum()函数,参数为一个字符串,如果这个字符串属于整数,浮点数或复数的表示,则返回True,否则返回False。
def isNum(string):
try:
complex(string) # 尝试将字符串转换为复数类型
return True
except ValueError:
pass
try:
float(string) # 尝试将字符串转换为浮点数类型
return True
except ValueError:
pass
try:
int(string) # 尝试将字符串转换为整数类型
return True
except ValueError:
pass
return False
# 测试示例
print(isNum("123")) # 输出: True
print(isNum("3.14")) # 输出: True
print(isNum("2+3j")) # 输出: True
print(isNum("-45.67")) # 输出: True
print(isNum("abc")) # 输出: False
print(isNum("1.23a")) # 输出: False
print(isNum("4+5i")) # 输出: False
- (2)、实现 isPrime()函数,参数为整数,要有异常处理。如果整数是质数,返回True,否则返回False。
"""
实现 isPrime()函数,参数为整数,要有异常处理。
如果整数是质数,返回True,否则返回False。
"""
def isPrime(num):
try:
if num <= 1: # 质数必须大于1
return False
for i in range(2, int(num**0.5) + 1): # 判断范围为2到sqrt(num)
if num % i == 0: # 如果能被整除,则不是质数
return False
return True # 没有在范围内找到能整除的因子,则是质数
except TypeError:
return False # 异常处理:如果无法转换为整数,则返回False
# 测试示例
print(isPrime(2)) # 输出: True
print(isPrime(17)) # 输出: True
print(isPrime(10)) # 输出: False
print(isPrime(0)) # 输出: False
print(isPrime(-5)) # 输出: False
print(isPrime("abc")) # 输出: False
print(isPrime(3.14)) # 输出: False
- (3)、编写一个函数,打印200以内的所有素数,以空格分割。
"""
编写一个函数,打印200以内的所有素数,以空格分割。
"""
def print_prime_numbers():
prime_numbers = []
for num in range(2, 201):
is_prime = True
for i in range(2, int(num**0.5) + 1):
if num % i == 0:
is_prime = False
break
if is_prime:
prime_numbers.append(num)
print(' '.join(str(num) for num in prime_numbers))
print_prime_numbers()