函数简介
函数是一系列代码的集合,用来完成某项特定的功能。函数可以增强代码的模块化和提高代码的重复利用率
函数的优点
-
避免代码的冗余
-
让程序代码结构更加清晰
-
让代码具有复用性,便于维护
函数定义及调用
格式
def 函数([参数, 参数, ...]): 函数体(重复的代码)
函数四部分
-
函数名:使用该函数的依据
-
函数体:完成功能的代码块
-
返回值:功能完成的反馈结果
-
参数:完成功能需要的条件信息
注意:
必须使用关键字def
函数体注意缩进
函数名()绑定
函数定义及调用的完整语法
# 1、定义 # def 是声明函数的关键字,后面跟着函数名,括号内是函数的参数 def 函数名(参数1,参数2,参数3,...): '''注释''' 函数体 # 函数体写具体的逻辑代码 return 返回的值 # retrun后面是函数的返回值,是函数体代码的运行成果 # 2、调用 函数名(参数1,参数2,参数3,...) '''注: -- 先定义再调用 -- 函数名存放的是函数地址 -- ()会触发函数体的执行 -- 函数执行完毕得到的是函数的返回结果,通常称之为函数的返回值,也称函数值 '''
函数的参数
1. 形参和实参的数量上必须保持一致
2. 命名关键字参数: 通过定义关键字获取实参的值,与形参的顺序无关。
3. 默认参数
①默认参数的使用场景:数据库连接,地址和端口号可以设置默认。账号密码登陆。
②使用默认参数的时候,如果给形参传递了实参,则形参会接收实参的值。如果没有给这个形参传递实参,则形参会采用默认值。
4. 可变参数(不定长参数)*args: 形参的数据会根据实参的数量的变化而变化
*args:接收N个位置参数,并且会把位置参数转换成元组的形式。(通俗点就是说args中保存的是没有利用的所有多余参数,保存方式为元组)
5. 关键字参数**kwargs:把N个关键字参数,转换成了字典
6. 在Python中定义函数,可以用必选参数、默认参数、可变参数、关键字参数和命名关键字参数,这5种参数都可以组合使用。参数定的顺序一般是:必选参数、默认参数、可变参数、命名关键字参数和关键字参数。
函数的返回值
用 def 语句创建函数时,可以用 return 语句指定应该返回的值,该返回值可以是任意类型。需要注意的是,return 语句在同一函数中可以出现多次,但只要有一个得到执行,就会直接结束函数的执行。
return 语句的作用:结束函数调用、返回值
指定返回值与隐含返回值
-
函数体中return语句有指定返回值返回的就是其值
-
函数体中没有 return 语句时,函数运行结束会隐含返回一个 None 作为返回值,类型是 NoneType,与 return 、return None 等效,都是返回 None
return 语句位置与多条 return 语句
-
python 函数使用 return 语句返回 "返回值",可以将其赋给其它变量作其它的用处;
-
所有函数都有返回值,如果没有 return 语句,会隐式地调用 return None 作为返回值;
-
一个函数可以存在多条 return 语句,但只有一条可以被执行,如果没有一条 reutrn 语句被执行,同样会隐式调用 return None 作为返回值;
-
如果有必要,可以显式调用 return None 明确返回一个None(空值对象)作为返回值,可以简写为 return,不过 python 中懒惰即美德,所以一般能不写就不写;
-
如果函数执行了 return 语句,函数会立刻返回,结束调用,return 之后的其它语句都不会被执行了
返回值类型
-
无论定义的是返回什么类型,return 只能返回单值,但值可以存在多个元素;
-
return [1,3,5] 是指返回一个列表,是一个列表对象,1,3,5 分别是这个列表的元素;
-
return 1,3,5 看似返回多个值,隐式地被Python封装成了一个元祖返回
函数的嵌套调用
-
一个函数里面又调用了另外一个函数,这就是所谓的函数嵌套调用
-
如果函数A中,调用了另外一个函数B,那么先把函数B中的任务都执行完毕之后才会回到上次 函数A执行的位置
def testB(): print('---- testB start----') print('这里是testB函数执行的代码...(省略)...') print('---- testB end----') def testA(): print('---- testA start----') testB() print('---- testA end----') testA()
内部函数的特点:
-
可以访问外部函数的变量
-
内部函数可以修改外部函数的可变类型的变量
-
内部函数修改全局的不可变类型的变量需要使用关键字global来声明变量
内部函数修改外部函数的不可变的变量时, 需要在内部函数中声明: nonlocal 变量名
-
locals() 查看本地变量有哪些, 以字典的形式输出
globals() 查看全局变量有哪些, 以字典的形式输出, (注意: 里面会有一些系统的键值对)
全局变量和局部变量
-
全局变量和局部变量的区别在于作用域,全局变量在整个py文件中声明,全局范围内可以使用;局部变量是在某个函数内部声明的,只能在函数内部使用,如果超出使用范围(函数外部),则会报错。
-
在函数内部,如果局部变量与全局变量变量名一样,则优先调用局部变量。
-
如果想在函数内部改变全局变量,需要在前面加上global关键字,在执行函数之后,全局变量值也会改变
-
如果全局变量是列表类型,可以通过list的列表方法去对列表进行修改,并且可以不用global来声明
函数闭包
闭包的概念
在一个外函数中定义了一个内函数,内函数里运用了外函数的临时变量,并且外函数的返回值是内函数的引用。这样就构成了一个闭包。
闭包的条件
1. 外部函数调用内部函数
2. 外部函数返回内部函数
3. 内部函数引用了外部函数的变量
闭包的陷阱
def my_func(*args): fs = [] for i in range(3): def func(): return i * i fs.append(func) # 1. 仅仅是记录函数名 return fs fs1, fs2, fs3 = my_func() # 2. 当外部函数被调用的时候, 解释器会根据函数内部声明的变量开辟变量空间, 但是此时还是不知道函数内部是什么, 只是知道有个叫func()的函数 print fs1() # 3. 当fs1()被调用的时候, 才开始注意到func中函数的内容, 而此时的i变成了2, 返回2*2=4 print fs2() print fs3() # 程序的结果并不是我们想象的结果0,1,4。实际结果全部是4
问题的关键就在于在返回闭包列表fs之前for循环的变量的值已经发生改变了,而且这个改变会影响到所有引用它的内部定义的函数
python解释原理:
-
在python解释器开始执⾏之后, 就会在内存中开辟⼀个空间, 每当遇到⼀个变量的时候, 就把变量名和值之间的关系记录下来,
-
但是当遇到函数定义的时候, 解释器只是把函数名读入内存, 表⽰这个函数存在了, ⾄于函数内部的变量和逻辑, 解释器是不关⼼的.
-
也就是说⼀开始的时候函数只是加载进来, 仅此⽽已, 只有当函数被调⽤和访问的时候, 解释器才会根据函数内部声明的变量来进⾏开辟变量的内部空间.
-
随着函数执⾏完毕, 这些函数内部变量占⽤的空间也会随着函数执⾏完毕⽽被清空
装饰器
作用:
当需要改变一个函数原有的功能时,但是不想/不能改变原来的函数,可以通过装饰器解决
使用:
-
装饰器其实就是一个函数,该函数有一个参数(函数类型),返回一个闭包
-
在返回的闭包中调用传递进来的函数,然后在调用函数的前后就可以添加内容
装饰器的通用写法
def decorator(func): def wrapper(*args, **kwargs): result = func(*args, **kwargs) return result return wrapper @decorator def func(*args, **kwargs): pass return
带参数的装饰器的写法
def outer(a): def decorate(func): def wrapper(*args, **kwargs): func(*args, **kwargs) return wrapper return decorate @outer(a) def func(): pass
注意: 如果装饰器是多层的, 那么装饰的顺序是由内向外开始装饰
命名空间和LEGB规则
-
命名空间
命名空间:就是指根据代码区域的不同而对变量名做出的划分, 在一个命名空间中往往会有一定的变量名和变量内容的对应关系。
-
LEGB规则
LEGB是指python中命名空间的四个从低到高不同的层次,分别是Local , Enclosing , Global , Built-in
-
Local:指一个函数或者方法的内部空间
-
Enclosing: 闭包内部空间
-
Global: 整个脚本的全局空间
-
Built-in: 指python最上层的系统自带的一些名字的空间。
-
-
解释器就会通过一定的顺序来查找这个变量名的具体内容。这个顺序就是
local --> enclosing --> global --> built-in
如果按照此顺序逐级而上却没有找到相关的变量名的内容的话就表明这个变量名在哪一级命名空间中都不存在,程序就会raise起一个NameError了
匿名函数
-
关键字lambda表示匿名函数,匿名函数跟普通函数一样也是函数对象,但函数体只能有一个表达式,函数返回值就是该表达式值,不能使用return,默号(:)前是函数参数,因此匿名函数不能实现复杂逻辑
-
普通函数会定义变量(函数名)并初始化为指向函数对象,匿名函数顾名思义没有函数名,因此匿名函数不会定义变量(函数名)
print reduce(lambda a, b: a + b, [1, 3, 5, 7, 9]) # 25
递归函数
在函数内部,可以调用其他函数。如果一个函数在内部调用自身本身,这个函数就是递归函数。
递归函数的两个重要阶段
-
递推:函数不断减少问题规模直至最终的终止条件。
-
回溯:拿到最终明确的值后,返回给上次调用进行处理,直至初始层。
在Python:
1、递归调用必须有一个明确的结束条件
2、在python中没有尾递归优化,递归调用的效率不高
3、进入下一次递归时,问题的规模必须降低
def fact(n): return fact_iter(n, 1) def fact_iter(num, product): if num == 1: return product return fact_iter(num - 1, num * product)