一、函数的定义
具有一个功能或者多个功能的代码块
函数的定义:可以重复使用,用来实现单一或者多个“功能”的代码段,以功能为单位;
函数的意义:提高程序的模块化管理,提高代码重复使用率;
函数的分类:内置函数(内建函数),自定义函数;
二、内置函数
可以直接调用而不需自定义的函数,使用时需要注意函数的参数、返回值等
print(abs(-100))
li = [1,5,3]
li.sort()
print(li)
输出:
100
[1, 3, 5]
三、自定义函数
1、语法格式
自定义函数格式:
def 函数名(【参数1,参数2】):
函数体
【return 返回值】
函数名:大写字母,小写字母、数字、_组合,注意不能以数字开头
函数占位符:pass
【】表示可有可无,函数只有被定义后才能被调用
用法如下:
def one():
pass
2、参数的使用
1)位置参数(必须参数)
一旦定义了位置参数,除默认参数外,必须传入,且必须按照定义的顺序传入
def print_char(num,line_num):
for i in range(line_num):
print("*"*num)
print_char(2,3)
输出:
**
**
**
案例中num参数和line_num为位置参数,传入参数时必须按照顺序进行参数传递
2)默认参数
当调用函数的是,不传入参数,会使用默认参数的值进行赋值
当默认参数跟位置参数一起使用时,位置参数需要放在前面
定义格式为:
def 函数(参数名=默认参数值,...):
pass
使用方式如下:
def print_char(num,line_num=3):
for i in range(line_num):
print("*"*num)
print_char(5)
print("===============")
print_char(5,2)
输出为:
*****
*****
*****
===============
*****
*****
当只传递num参数时,line_ num参数默认为3
当既传递了num参数又传入line_ num参数时,以传入参数为准。
当函数默认参数是可变类型时
def fun1(a=[]):
a.append(20)
print(a)
fun1()
fun1()
fun1([1,2,3])
fun1()
输出为:
[20]
[20, 20]
[1, 2, 3, 20]
[20, 20, 20]
对比默认参数为不可变类型时
def fun1(a=1):
a+=1
print(a)
fun1()
fun1()
fun1(2)
fun1()
输出为:
2
2
3
2
结论:默认参数,不能绑定可变类型。只能使用不可变数据类型。
定义函数后,每次编译函数都会为默认参数指定固定的内存地址,当使用可变参数传递时,执行函数的时候默认参数的值也在发生改变,而采用不可变的默认参数时,无论执行多少次,默认参数的值还是指向定义的值
3)命名关键字参数
格式:
def 函数名(* , 参数名,参数名)
*,后面的都是必须以 参数名=参数值
调用:如果定义了命名关键字参数,就必须以 参数名=参数值 传入参数
def print_char(num,*,line_num):
for i in range(line_num):
print("*"*num)
print_char(2,line_num=3)
输出:
**
**
**
命名关键字参数作用:
(1)增加程序可读性
(2)忽略参数的顺序
(3)当参数有默认值时,调用变得更简单
注意:
(1)如果定义了命名关键字参数,除了有默认值的情况,否则同位置参数一样,必须传入
(2)在顺序上,如果还有位置参数,那么命名关键字参数一定放在位置参数的后面
4)可变参数( args)*
可变参数是用来收集所有的位置参数,形成一个元组
用法如下:求n个数的平方和
def sum2(*args):
sum = 0
for i in args:
sum+=i**2
return sum
print(sum2(1,2,3,4,5))
输出:
55
定义函数时,*表示将收集到的参数打包成一个元组
当输入的参数本身就是元组或列表时,要使用 * 先进行解包,用法如下:
def sum2(*args):
sum = 0
for i in args:
sum+=i**2
return sum
s1 =[1,2,3,4,5]
print(sum2(*s1))
输出:
55
5) 关键字参数( kwargs)**
收集所有的 命名关键字参数,打包形成字典,传递给函数的形式参数
使用方法如下:
def register(num,name,**kwargs):
print(num,name)
for k,v in kwargs.items():
print(k,v)
register(100,"张三")
register(101,"李四",gender = "男",height=180)
输出:
100 张三
101 李四
gender 男
height 180
同理,如果传入的参数是字典时,需要先使用**进行解包
使用如下:
def register(num,name,**kwargs):
print(num,name)
for k,v in kwargs.items():
print(k,v)
register(100,"张三")
d1 = {"gender":"男","height":180}
register(101,"李四",**d1)
输出:
100 张三
101 李四
gender 男
height 180
6)位置参数 默认参数 命名关键字 可变参数 关键字参数总结
参数组合使用时需要注意的时:
(1)位置参数要放在最前面
(2)命名关键字参数不能和可变参数放一起使用
(3)如果希望在可变参数后面继续加参数,可以通过参数名=参数值的形式传递
(4)位置参数> 命名关键字参数/可变参数 > 关键字参数
相关规则验证如下:
def f1(a,b,c=0,*args,**kwargs):
print("a={},b={},c={},args={},kwargs={}".format(a,b,c,args,kwargs))
def f2(a,b,c=0,*,d,**kwargs):
print("a={},b={},c={},d={},kwargs={}".format(a,b,c,d,kwargs))
f1(1,2)
f1(1,2,3)
f1(1,2,3,4,5)
f1(1,2,3,4,5,x=99)
f2(1,2,d=3)
f2(1,2,-1,d=3,e=4,f=5)
f2(1,2,-1,e=4,f=5,d=3)
输出:
a=1,b=2,c=0,args=(),kwargs={}
a=1,b=2,c=3,args=(),kwargs={}
a=1,b=2,c=3,args=(4, 5),kwargs={}
a=1,b=2,c=3,args=(4, 5),kwargs={'x': 99}
a=1,b=2,c=0,d=3,kwargs={}
a=1,b=2,c=-1,d=3,kwargs={'e': 4, 'f': 5}
a=1,b=2,c=-1,d=3,kwargs={'e': 4, 'f': 5}
万能参数
def f(*args,**kwargs):
四、函数的返回值
所以函数都要返回值
以下三种情况返回值都是None
(1)return None
(2)return
(3)没有return
使用return注意:
(1)当程序执行return,相当于函数执行结束了。
(2)return可以返回多个值,是元组的形式返回,返回值可以使用单个变量获取,
(3)还可以使用跟返回值个数相同的变量分别获取(必须一一对应)
(4)返回值一般采用元组的形式,不采用可变类型作为返回值
def f1(a,b):
return a+b,a*b
x,y=f1(2,3)
print(x,y)
输出如下:
5 6
五、函数值传递
python分为可变类型值传递和不可变类型值传递
def myfun(a):
a=a+2
print("不可变值传递:在myfun的函数中a=",a)
a=3
myfun(a)
print("不可变值传递:在主程序中a=",a)
# 可变类型的值传递
def myfun(a):
a[0]="new"
print("可变值传递:在myfun函数中a",a)
a=[1,2,3,4]
myfun(a)
print("可变值传递:在主程序中a=",a)
输出:
不可变值传递:在myfun的函数中a= 5
不可变值传递:在主程序中a= 3
可变值传递:在myfun函数中a ['new', 2, 3, 4]
可变值传递:在主程序中a= ['new', 2, 3, 4]
图解如下:
六、命名空间和作用域
命名空间:可以保存名字的容器,名字包括:变量名、函数名、类名…
在计算机中,在命名空间中存储名字是以 键–值 存储,以字典结构存储
键—变量名
值—引用内存的对象地址
对于函数而言:
第一次def 就决定了,存在在哪个命名空间中
对于变量而言:
第一次赋值的时候,决定了在哪个命名空间中
每一种命名空间都有自己的区域,当定义的函数、变量或者其他的命名定义到区域内之后,就意味着是对应的命名空间。
1)命名空间的分类
a)内建命名空间:在python解释器启动的时候,就会创建的命名空间,python解释器关闭时销毁
如:内建函数print sum
b)全局命名空间:在读取模块定义的时候创建,当程序运行结束的时候 ,全局命名空间销毁。
如: 全局变量,顶层函数
c)局部命名空间:在函数创建的时候,被创建,当函数执行结束之后,销毁。
如:局部变量,函数的参数,嵌套函数…
需要注意的是:命名空间千万不要理解成包含的关系。区别只是在于作用域不同。
2)作用域
作用域:命名空间中的名字起作用的范围。
a)内建命名空间:所有模块。作用域最大。
b)全局命名空间:在整个py文件中。如果需要使用其他全局命名空间的内容,需要import
c)局部命名空间:只在当前函数内部有效。
需要注意的是:作用域也不是包含关系。只是作用范围的大小。
3)访问原则
a)LEGB原则
L–Local— 局部作用域
E–Enclosing—外围作用域
G–global---- 全局作用域
B–buildin— 内建作用域
① python先到局部命名空间中查找x,如果没有具体的变量或者函数,才去下一个命名空间中查找
如果找到了,则停止查找
② 如果在局部命名空间中找不到,到外围命名空间中查找,如果找到了,则停止,否则去下一个命名空间
③ 如果在外围命名 空间中没找到,则到全局命名空间中查找,如果找到,则停止,否则去下一个命名空间
④ 如果在全面命名空间中没找到,则到内建命名空间中查找
⑤ 如果内建命名空间中仍然没有找到,则报nameError异常
b) 对名称的访问
读取:完全按照LEGB原则
修改:只从当前最小的作用域查找,如果找到,则修改,找不到,
不会继续向上找,而是在当前的作用域中新建
如果必须要修改,可以通过global nonlocal
删除:无论如何也只能删除当前空间的名字
c)global nonlocal
在局部命名空间中,修改全面命名空间的内容 global
i = 2
def fun1():
global i
i=1
fun1()
print(i)
输出:
1
在局部命名空间中,修改外围命名空间中的变量 nonlocal
def fun1():
i=1
print("外层函数i={}".format(i))
def fun2():
nonlocal i
i = 2
print("内层函数i={}".format(i))
fun2()
print("最终输出i={}".format(i))
fun1()
输出:
外层函数i=1
内层函数i=2
最终输出i=2
d)locals() 、globals():
locals() :找到当前命名空间的名字
globals():全局命名空间中的名字
print(locals())
print(globals())
e)练习
info="address:"
def fun1(country):
def fun2(area):
city="北京"
print(info+country+city+area)
city="上海"
fun2("海淀")
fun1("中国")
输出:
address:中国北京海淀
七、lambda()表达式
1、头等函数: 变量和函数名一样,也可以像变量名一样,可以被赋值、被传递、可以作为返回值
a)函数被赋值
def print_world():
print("hello world!")
print_new = print_world
print_new()
输出:
hello world!
b)函数被传递
def fun1(k):
print("执行fun1")
k()
def fun2():
print("执行fun2")
fun1(fun2)
输出:
执行fun1
执行fun2
2、lambda()函数
lambda()函数格式:lambda 参数:返回式
以下两种功能一致
def add_1(k):
return k+1
print(add_1(2))
x = lambda k:k+1
print(x(2))
输出:
3
3
lambda 是在函数体比较简单的时候,适合使用,可以作为key时使用,如下:
s=lambda k:k+1
print(s(-20))
li=[1,2,5]
li.sort(key=s)
li.sort(key=lambda k:k+1)
print(li)
八、函数的递归
函数的递归即指函数调用自己本身,有直接递归和间接递归两种
直接递归:A–A
间接递归:A—B---A
递归一定要有条件,来终止递归,否则递归没有意义
递推 回归
递推和循环相比:循环的思路比较复杂,递归思路简单,执行时间比循环长。
1、递归实现
如实现100以内的数进行相加:
a)循环实现
s= 0
for i in range(1,101):
s+=i
print(s)
输出:
5050
b)递归实现
sum =0
def a(i):
if i ==1:
sum=1
else:
sum=a(i-1)+i
return sum
print(a(100))
输出:
5050
递归练习:计算裴波那契数列中第n个数的数值
def fun1(n):
if n == 1 or n == 2:
return 1
else:
return fun1(n-1)+fun1(n-2)
print(fun1(6))
输出:
8
2、递归深度
递归不仅仅包含自己调用自己,还包含调用其他函数
查看递归深度限制
import sys
print(sys.getrecursionlimit())
输出:
1000
默认是1000
如需修改的话,可采用如下格式
sys.setrecursionlimit(10)
九、函数的注释和文档
函数格式为:
def fun():
"""
功能介绍
:return: 返回值的 介绍
"""
pass
如需查看函数的注释部分,可采用如下语句:
print(fun.__doc__)
help(fun)
另外一种注释方式如下:
def fun(a:int,b:int)->int:
"""
功能介绍
:return: 返回值的 介绍
"""
pass
print(fun.__annotations__)
输出:
{'a': <class 'int'>, 'b': <class 'int'>, 'return': <class 'int'>}
表示参数a的注释是int,b参数的注释是int,函数返回值return为int。