函数的定义与调用
函数概述
Python语言中使用函数分为两个步骤:定义函数和调用函数
定义函数
即根据函数的输入、输出和数据处理完成函数代码的编写。定义函数只是规定了函数会执行什么操作,但并不会真正去执行。
变量名、函数名、类名等都是Python语言的自定义标识符,命名规则完全相同。
调用函数
即真正去执行函数中的代码,是指根据传入的数据完成特定的运算,并将运算结果返回到函数调用为止的过程。
例:函数定义和调用示例。
def CalCircleArea(): # 定义名字为CalCircleArea的函数
s=3.14*3*3
print('半径为3的圆的面积为:%.2f'%s)
CalCircleArea() #调用函数CalCircleArea
半径为3的圆的面积为:28.26
形参和实参
形参的全称是形式参数,即定义函数时函数名后面的一对小括号中给出的参数列表。
形参只能在函数中使用,其作用是接收函数调用时传入的参数值(实参),并在函数中参与运算。
例:圆面积函数和长方形面积函数的定义。
def CalCircleArea(r): # 定义名字为CalCircleArea的函数,r为形参
s=3.14*r*r # 计算半径为r的圆的面积。
print('半径为3的圆的面积为:%.2f'%s)
def CalRectArea(a,b):
s=a*b # 计算边长分别为a和b的长方形面积。
print('边长为%.2f和%.2f的长方形的面积为:%.2f'%(a,b,s))
CalCircleArea(3) # 调用函数CalCircleArea,将实参3传递给对应的形参r。
CalRectArea(5,10) # 调用函数CalRectArea,将实参5传递给对应的形参a,10传递给b。
实参的全称是实际参数,即在调用函数时函数名后面的一对小括号中给出的参数列表。
当调用函数时,会将实参的值传递给对应的形参,函数中再利用形参做运算,得到结果。
注意:实参名和形参名不需要相同,在传递时根据位置一一对应。当有多个实参时,使用逗号分隔。
默认参数和关键字参数
函数的默认参数就是缺省参数,即当调用函数时,如果没有为某写形参传递对应的实参,则这些形参会自动使用默认参数值。
例:带默认参数的函数的定义和调用方法示例。
def StudentInfo(name,country='中国'): # 参数country的默认参数值为'中国'
print('姓名:%s,国家:%s'%(name,country))
StudentInfo('李晓明') # 这里没有给country传实参值,但因为有默认参数所以不会出错
StudentInfo('大卫','美国') # 给country传了实参,则不再使用默认参数
姓名:李晓明,国家:中国
姓名:大卫,国家:美国
在调用函数时,除了前面那种通过位置来体现实参和形参的对应关系的方法(即位置参数),还有一种使用关键字参数的方法,其形式为“形参=实参”。
注意:位置参数和关键字参数可以混合使用,但位置参数必须在前,关键字参数在后。
例:关键字参数使用方法示例。
def StudentInfo(name,chineselevel='良好',country='中国'):
print('姓名: %s, 中文水平: %s, 国家: %s'%(name,chineselevel,country))
StudentInfo('李晓明')
StudentInfo('大卫','美国')
StudentInfo(country='美国',chineselevel='一般',name='约翰')
姓名: 李晓明, 中文水平: 良好, 国家: 中国
姓名: 大卫, 中文水平: 美国, 国家: 中国
姓名: 约翰, 中文水平: 一般, 国家: 美国
不定长参数
*不定长参数名:表示这个不定长参数对应的是一组位置参数。(一个*号)
** 不定长参数名:表示这个不定长参数对应的是一组关键字参数。(两个**号)
- 即在调用函数时可以接收任意数量的实参,这些实参在传递给函数时会被封装成元组(位置参数)或字典(关键字参数)形式。
- 一般情况下,不定长参数是放在形参列表的最后,前面传入的实参与普通形参一一对应,而后面剩余的实参会被封装成元组或字典后传给不定长参数。
- 对应使用位置参数形式的不定长参数,Python也允许普通形参放在不定长参数后面,但此时要求在调用函数时必须使用关键字参数方式给不定长参数后面的形参传递实参。
- 对于有默认参数的形参,在调用函数时也可以不传入相应实参。
语法格式
def cheeseshop(kind, *arguments):
pass
def foo(name, **kwds):
pass
例:两种不定长参数使用方法示例。
def StudentInfo1(name,*args): # *args只能接收一组位置参数,如果传入关键字参数就会出错
print('姓名:',name, '其他:',args)
def StudentInfo2(name,**args): # **args只能接收一组关键字参数,如果传入位置参数就会出错
print('姓名:',name, '其他:',args)
def StudentInfo3(name,*args,country='中国'):
print('姓名:',name, '国家: ',country,'其他:',args)
StudentInfo1('李晓明','良好','中国')
StudentInfo2('李晓明',中文水平='良好',国家='中国')
StudentInfo3('李晓明',19,'良好')
StudentInfo3('大卫',19,'良好',country='美国')
姓名: 李晓明 其他: ('良好', '中国') # 元组
姓名: 李晓明 其他: {'中文水平': '良好', '国家': '中国'} # 字典
姓名: 李晓明 国家: 中国 其他: (19, '良好')
姓名: 大卫 国家: 美国 其他: (19, '良好')
拆分参数列表
如果一个函数所需要的参数已经存储在了列表、元组或字典中,则可以直接从列表、元组或字典中拆分出来函数所需要的这些参数。
其中列表、元组拆分出来的结果作为位置参数,而字典拆分出来的结果作为关键字参数。
*args
**args
例:通过拆分方法传递参数示例。
def sumVal(*args):
sum=0
for i in args:
sum +=i
print('求和结果为: ',sum)
ls=[3,5.2,7,1]
sumVal(ls[0],ls[1],ls[2],ls[3]) # 不通过拆分方法传递参数示例,
sumVal(*ls) # 通过拆分方法传递参数示例。 将ls列表或元组拆分出来的结果作为位置参数。
求和结果为: 16.2
求和结果为: 16.2
例:字典拆分结果作为函数关键字参数示例。
def StudentInfo(name,chineselevel,country):
print('姓名: %s,中文水平: %s, 国家: %s'%(name,chineselevel,country))
d={'country':'中国','chineselevel':'良好','name':'李晓明'}
StudentInfo(**d)
姓名: 李晓明,中文水平: 良好, 国家: 中国
返回值,return语句
如果希望能够将一个函数的运算结果返回到调用函数的位置,以使得可以继续用该运算结果再去参与其他运算,此时则应使用return语句。
如果没有显示地写return语句,return None(或直接写为return)
模块概述和import语句
Python提供了交互式和脚本式两种运行方式。当要执行的代码比较长、且需要重复使用时,我们通常将代码放在扩展名为.py的Python脚本文件中。
按照代码功能的不同,将代码分门别类地放在不同脚本文件中,这些脚本文件就称为模块(Module)。
import module1
import module2
...
或
import module1,module2,...moduleN
当要使用一个模块中的某些功能时,我们可以通过import方式将该模块导入。如果要使用该模块中定义的标识符,则需要通过“模块名.标识符名”的方式。
模块名.函数名
# fibo.py文件
def PrintFib(n):
a,b=1,1 # 将a和b都赋为1
for i in range(1,n+1): # i的取值依次为1,2,...n
print(a,end=' ') # 输出斐波那契数列的第i项
a,b=b,a+b # 更新斐波那契数列第i+1项的值,并计算第i+2项的值。
print() # 输出一个换行
def GetFib(n):
fib=[]
a,b=1,1
for i in range(1,n+1):
fib.append(a)
a,b=b,a+b
return fib
if __name__=='__main__':
PrintFib(10)
ls=GetFib(10)
print(ls)
1 1 2 3 5 8 13 21 34 55
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
# testfibo.py
import fibo # 导入fibo模块(就是fibo.py这个文件)
fibo.PrintFib(5) # 调用fibo模块中的Print函数,模块名.函数名
ls=fibo.GetFib(5) # 调用fibo模块中的GetFib函数
print(ls)
from…import
除了使用import将整个模块导入,也可以使用from import将模块中的标识符(变量名、函数名)直接导入当前环境,这样我们在访问这些标识符时就不再需要指定模块名。
如果需要导入一个模块中的所有标识符,也可以使用from 模块名 import * 的方式。
from 模块名 import 标识符1,标识符2,...,标识符N # 导入指定标识符(函数)
from 模块名 import * # 导入模块中的所有标识符(函数)
from fibo import PrintFib,GetFib # 导入Fibo模块中的PrintFib和GetFib
PrintFib(5) #忽略fibo模块名直接调用PrintFib函数(之前使用模块名.函数名,如:fibo.PrintFib(n))
ls=GetFib(5)
print(ls)
all
如果一个模块定义了列表__all__,则只能导入__all__列表中存的标识符。
例:对于fibo.py脚本文件,在第一行加入__all__[‘PrintFib’],则通过“from fibo import *”只能导入fibo模块中的PrintFib。
# fibo.py文件中第一行加入
__all__=['PrintFib']
as别名
无论是利用import导入模块,还是from import导入模块中的标识符,在导入的同时都可以使用as为模块或标识符起别名。
from fibo import PrintFib as pf # 导入fibo模块中的PrintFib,并重命名为pf
pf(2)
模块搜索路径
当导入一个名为 spam 的模块时,解释器首先会搜索具有该名称的内置模块。
这些模块的名称在 sys.builtin_module_names 中列出。 如果未找到,它将在变量 sys.path 所给出的目录列表中搜索名为 spam.py 的文件。 sys.path 是从这些位置初始化的:
- 被命令行直接运行的脚本所在的目录(或未指定文件时的当前目录)。
- PYTHONPATH (目录列表,与 shell 变量 PATH的语法一样)。
- 依赖于安装的默认值(按照惯例包括一个 site-packages 目录,由 site 模块处理)。
dir()函数
内置函数 dir() 用于查找模块定义的名称。返回结果是经过排序的字符串列表:
例:没有参数时,dir()列出当前已定义的名称。
a = [1, 2, 3, 4, 5]
import fibo
fib = fibo.fib
dir()
['__builtins__', '__name__', 'a', 'fib', 'fibo', 'sys']
# 注意它列出所有类型的名称:变量,模块,函数,……
全局变量__name__和系统模块
每个模块中都有一个全部变量__name__。
作用:获取当前模块的名称,如果当前模块式单独执行的,则其__name__的值就是__main__;否则,如果是作为模块导入,则其__name__的值就是模块的名字。
1、module.py脚本文件
print(__name__) # 输出全局变量__name__的值为__main__
2、testmodule.py
import module # 将上面module.py文件作为模块导入
print(__name__) # 输出为module
包
包是通过使用“带点号模块名”来构造 Python 模块命名空间的一种方式。 例如,模块名 A.B 表示名为 A 的包中名为 B 的子模块。
导入包时,Python 搜索 sys.path 里的目录,查找包的子目录。
需要有 init.py 文件才能让 Python 将包含该文件的目录当作包来处理
init.py文件
可以是一个空文件,也可以包含包的初始化代码或设置__all__列表。
sound/ 顶级包
__init__.py 初始化这个声音包
formats/ 文件格式转换子包
__init__.py
wavread.py
wavwrite.py
aiffread.py
aiffwrite.py
auread.py
auwrite.py
...
effects/ 音效子包
__init__.py
echo.py
surround.py
reverse.py
...
filters/ 过滤器子包
__init__.py
equalizer.py
vocoder.py
karaoke.py
...
# 要使用sound包的effects子包的echo模块
import sound.effects.echo
# 假设在echo模块中有一个echofilter函数,则调用函数时必须指定完成的名字(包括各层的包名和模块名)
sound.effects.echo.echhofilter(实参列表)
# 也可以使用from import 方式导入包中的模块,调用函数时不需要加包名,如:
from sound.effects import echo
echo.echofilter(实参列表)
# 使用from import 也可以直接导入模块中的标识符,如:
from sound.effects.echo import echofilter
# 此时调用echofilter函数可以直接写作:
echofilter(实参列表)
猴子补丁和第三方模块获取安装
猴子补丁是指在运行时动态替换已有的代码,而不需要修改原始代码。
def Sum(a,b):
print('Sum函数被调用')
return a+b
def NewSum (*args):
print('NewSum函数被调用!')
s=0
for i in args:
s+=i
return s
Sum=NewSum # 将NewSum赋给Sum,后面再调用Sum函数,实际上就是执行NewSum函数。
print(Sum(1,2,3,4,5))
NewSum函数被调用!
15
变量的作用域和局部变量
指变量的作用范围,即定义一个变量后,在哪些地方可以使用这个变量。
按照作用域的不同,Python中的变量可以分为局部变量和全局变量
局部变量
在一个函数中定义的变量就是局部变量(包括形参),其作用域是从定义局部变量的位置至函数结束位置。
def localVar1(x):
print('localVar1中的x值为:',x)
x=100
print('localVar1中修改后的x值为:',x)
y=20
print('localVar1中的y值为:', y)
def localVar2():
x=10
print('localVar2中调用localVar1前x的值为:',x)
localVar1(15)
print('localVar2中调用localVar1后x的值为:',x)
localVar2()
localVar2中调用localVar1前x的值为: 10
localVar1中的x值为: 15
localVar1中修改后的x值为: 100
localVar1中的y值为: 20
localVar2中调用localVar1后x的值为: 10
全局变量和global关键字
全局变量
在所有函数外定义的变量就是全局变量,其在函数中都可以使用。
def GlobalVar1():
print('GlobalVar1中x的值为:',x) # 输出全局变量x=20
def GlobalVar2():
x=100 # 函数中定义的为局部变量
print('GlobalVar2中x的值为:',x) # 输出局部变量x=100
x=20 # 函数外定义,全局变量
GlobalVar1()
GlobalVar2()
GlobalVar1()
GlobalVar1中x的值为: 20 # 全局
GlobalVar2中x的值为: 100 # 局部
GlobalVar1中x的值为: 20 # 全局
global关键字
在一个函数中要修改全局变量的值,必须使用global关键字声明使用该全局变量。
def GlobalVar1():
print('GlobalVar1中x的值为:',x) # 输出全局变量
def GlobalVar2():
global x # 声明在GlobalVar2函数中使用的是全局变量x
x=100 # 将全局变量x赋为100
print('GlobalVar2中x的值为:',x) # 输出全局变量x
x=20 # 全局变量
GlobalVar1()
GlobalVar2()
GlobalVar1()
GlobalVar2中x的值为: 100 # 全局,被修改后的值
GlobalVar1中x的值为: 20 # 全局
GlobalVar1中x的值为: 20
nonlocal关键字
在Python中,函数的定义可以嵌套。通过nonlocal关键字,可以使内层的函数直接使用外层函数中定义的变量。
例:不使用nonlocal关键字示例。
def outer():
x=10
def inner():
x=20
print('inner函数中的x值为:',x)
inner()
print('outer函数中的x值为:',x)
outer()
inner函数中的x值为: 20
outer函数中的x值为: 10
例:使用nonlocal关键字示例。
def outer():
x=10
def inner():
nonlocal x # noncal声明x
x=20 # 修改外层函数outer的x值
print('inner函数中的x值为:',x) # 输出20
inner()
print('outer函数中的x值为:',x) # 输出20,因为在inner函数中通过nonlocal关键字声明且重新赋值为20
outer()
inner函数中的x值为: 20
outer函数中的x值为: 10
递归函数
指在一个函数内部通过调用自己来完成一个问题的求解。
def fac(n):
if n==1: # 如果要计算1的阶乘,则直接返回1(结束递归函数调用的条件 )
return 1
return n*fac(n-1) # 将计算n!分解为n*(n-1)!
print(fac(5)) # 调用fac函数计算5的阶乘并将结果输出到屏幕,输出120
'''
fac(5)=>5*fac(4)=>5*(4*fac(3))=>5*(4*(3*fac(2)))=>5*(4*(3*(2*fac(1))))
=>5*(4*(3*(2*1)))=>5*(4*(3*2))=>5*(4*6)=>5*24=>120
'''
def fac(n):
f=1 # 保存阶乘结果
for i in range(2,n+1): # 依次取值为2至n
f*=i
return f
print(fac(5))
高阶函数
指把函数作为参数的一种函数。
函数不仅可以赋给形参,也可以赋给普通变量。赋值后,即可以用变量名替代函数名完成函数调用。
例:高阶函数示例。
def FunAdd(f,x,y):
return f(x) + f(y) # 用传给f的函数先对x和y分别处理后,再求和并返回
def Square(x):
return x**2 # 返回x的平方
def Cube(x):
return x**3 # 返回x的立方
Print(FunAdd(Square,3,-5))
Print(FunAdd(Cube,3,-5))
lambda函数
也称为匿名函数,是一种不使用def定义函数的形式。
作用:能快速定义一个简短的函数。
lambda函数的函数体只是一个表达式,所以lambda函数通常只能实现比较简单的功能。
lambda 参数1,参数N : 表达式
例:lambda函数示例。
lambda a, b: a+b # 函数返回两个参数的和
def FunAdd(f,x,y):
return f(x) + f(y) # 用传给f的函数先对x和y分别处理后,再求和并返回
Print(FunAdd(lambda x:x**2,3,-5))
Print(FunAdd(lambda x:x**3,3,-5))
闭包
作用:可以封存函数执行的上下文环境。
如果内存函数使用了外层函数中定义的局部变量,并且外层函数的返回值是内层函数的引用,就构成了闭包。
定义在外层函数中,但由内层函数使用的变量被称为自由变量。
闭包是一种特殊情况,外层函数在结束时会发现其定义的局部变量将来会在内层函数中使用,此时外层函数就会把这些自由变量绑定到内层函数。
所谓闭包,实际上就是将内层函数的代码以及自由变量(外层函数定义、但会由内层函数使用)打包在一起。
例:闭包示例。
def outer(x):
y=10 # 定义局部变量y并赋为10
def inner(z): # 在outer函数中定义嵌套函数innter
nonlocal x,y # nonlocal声明,使用外层函数outer函数中定义的y=10
return x+y+z
return inner # 返回嵌套函数inner的引用
f=outer(5) # 将返回的inner函数赋给f
g=outer(50) # 将返回的inner函数赋给g
'''
通过两次调用outer函数形成了两个闭包,这两个闭包具有相互独立的上下文环境
(一个闭包中x=5,y=10;另一个闭包中x=50,y=10),且每个闭包可多次调用。
'''
print('f(20)的值为:',f(20))
print('g(20)的值为:',g(20))
print('f(20)的值为:',f(20))
print('g(30)的值为:',f(30))
f(20)的值为: 35
g(20)的值为: 80
f(20)的值为: 35
g(30)的值为: 45