前言:
函数在编程中有着至关重要的地位,它组织代码、使代码复用,下面我们将分类介绍一下Python函数的基本使用、闭包、装饰器等;
函数的使用阶段:
定义阶段:定义就是把一段代码,赋值给函数
调用阶段:一定要先定义,再调用;
函数的组成结构:
1、函数名
2、 函数体
3、返回值 (1-3属于函数定义阶段)
4、调用阶段
5、表达式形式调用函数可以保存函数返回值 / 语句形式调用函数执行后的返回值 不会被保存下来;
def my_sum(x,y): #def语句 定义的函数名 res=x if x > y else y #函数体 return res #函数的执行结果,人过留名,雁过留声;函数执行结束后,如果没有返回值,就是 none a=my_sum(7,8) # 函数调用阶段 把函数执行的结果,赋值给变量,保存起来 print(a) #打印函数的执行结果 print(my_sum(8,9)) #语句形式 调用函数,不会保存函数的返回值;
函数的参数
函数的可变参数: *args **args (args是变量名可以为任何字符)
在实参的角度
*arga:在按位置传值的情况下,多余的 组织成 元组 值赋值给*args 包含多个位置参数
1 def chen (*b): # *b传入形参的是元组,其长度可以无穷无尽。 b=(1,2,3,4,5,6,7) 2 sum=0 3 for i in b: 4 sum+=i 5 return sum 6 7 print(chen(1,2,3,4,5,6,7))
在形参的角度
形参混合使用优先级:位置传参、args传参、默认传参
def foo (a,*b,c=2): # *b 在形参的角度来讲属于 位置传参 所以一定要在 默认传参之前 print(a) print(*b) print(c) foo(1,2,4,5,6,c=7)
*(1,2,3,4)或者 *[1,2,3,4]就相当于 把元组、列表这些序列 分解、打散 分别给实参;
def foo (a,b,c,d):
print(a)
print(b)
print(c)
print(d)
foo( *(1,2,3,4)) # *(1,2,3,4) 元组也可以在实参数上 传值 *(1,2,43,4)== *args
**arges
从实参角度
在关键字传值时,把多余的实参 组织成 字典 形式 赋值给形参
1 def foo (a,**b): #从形参的角度来说 在关键字传值得情况下,把多余的实参赋给 **b 2 print(a) 3 print(b) 4 5 6 foo( 1,b=2,c=3 ,d=4)
运行结果:
1
{'b': 2, 'c': 3, 'd': 4}
(*a,**b): #接受任意 实参 ,可以位置传参,可以是关键字传参
1 def foo (*a,**b): #形参可以接受任意实参 ,可以是位置传参,可以是关键字传参 2 print(a) 3 print(b) 4 5 6 foo( 1,2,3,4,5,b=2,c=3 ,d=4) 7 8 运行结果: 9 (1, 2, 3, 4, 5) 10 {'b': 2, 'c': 3, 'd': 4}
闭包
前戏
x=1 def f1(): x=2 def f2(): print(x) return f2 f3=f1() x=2222222222 f3() #函数的作用域已经在函数定义阶段就已经确立了,调用函数阶段不管函数在哪个位置执行,都要回到原来确定好作用域;
执行结果:2
个人理解闭包函数的作用 (实现装饰器):
闭包可以保存住外部函数的变量(状态),既然内部函数打包了外部作用域的状态,在这个基础上增加外部函数的功能,这不是正是函数装饰器吗?
x=1 def f1(): x=2 y=6 def f2(): print(x) print(y) return f2 f3=f1() #f3 =f1内部的f2 x=2222222222 f3() ''' 函数的作用域已经在函数定义阶段就已经确立了,调用函数阶段不管函数在哪个位置执行, 都要回到原来定义的位置寻找作用域关系! ''' print(f3.__closure__[0].cell_contents ) #获取内部函数引用外部作用域的值 print(f3.__closure__[1].cell_contents )
函数装饰器(实现装饰器的原理就是闭包函数)
1、在不改变现有代码的情况下,增加函数的功能;
2、@函数名A = 把正下方的原始函数B,当A函数的参数传入A,并把A的return的结果,赋值给 函数B; B=return的结果 A(B)
函数B
无参数装饰器函数
def auth(fun): def wrapper(*args,**kwargs): print("开始认证") f=fun(*args,**kwargs) print("结束") return wrapper @auth #@语法糖的的操作:把正下方的函数,当做一个参数,传给auth,并把index函数指向auth的的执行结果; 无参装饰器auth不执行;
def index():
print("主页") index()
有参装饰器
def auth1(name): def auth(fun): def wrapper(*args,**kwargs): print("%s 开始认证"% name) f=fun(*args,**kwargs) print("结束") return wrapper return auth @auth1(name='alex') #1、 当Python解释器遇到函数名+括号auth1(name='alex'),先让auth1先执行,执行完毕,得出执行结果。 #2、@auth1(name='alex')的执行结果,也就是return的函数, auth函数. #3、所以有参数装饰器 @是auth函数, 也就是把正下方的函数,当做一个参数,传给auth()的执行结果;wrapper函数 有参装饰器 def index(): print("主页") index()
装饰器的缺陷
原始函数的帮助文档会被装饰器函数覆盖,解决之道
from functools import wraps def auth(func): @wraps(func) #使用内置函数wraps,装饰一下原始函数,会保存住原始函数的帮助信息
from functools import wraps def auth(func): @wraps(func) #使用内置函数wraps,装饰一下原始函数,会保存住原始函数的帮助信息 def wrapprt(*args,**kwargs): ''' 我是1个装饰器函数 ''' print('开始认证。。。') res=func() print('结束认证。。。') return res return wrapprt @auth def index(): ''' 我是1个需要被装饰的函数 ''' print('主页') index() print(index.__doc__)
递归函数:
递归经常和迭代一起被提到,(迭代就是从复做某事);递归:函数调用自身,每次调用自身一次,问题规模比上一次减少,最后有一个明确的结束条件;
递归就是函数执行时,调用了自己,然后把这种状态积累保存在栈内存里,遇到自己执行的结果了,一起返回出来;
吃1+吃2+吃3 函数有了执行结果 4 的时候 吐出来:吃进去的1+吃进去2+吃进去3+函数结果= 10
堆内存:吃1+吃2+吃3,然后吐出来顺序;1------->2------->3
栈内存:吃1+吃2+吃3,然后吐出来顺序;3------>2-------->1
队列:吃了就拉出来,不积累;吃一个拉一个 1---->
def age(n): #age()=10 print('=========',n) if n==1: return 10 else: return age(n-1)+2 print(age(5))
data = [1, 3, 6, 7, 9, 12, 14, 16, 17, 18, 20, 21, 22, 23, 30, 32, 33, 35]
def seach(number,data): print(data) mid_index = int(len(data)/2) mid_values = data[mid_index] # print(data) if number> mid_values: data=data[mid_index:] seach(number,data) elif number < mid_values: data=data[:mid_index] seach(number,data) else: print('find it') return seach(3,data)
执行结果:
[1, 3, 6, 7, 9, 12, 14, 16, 17, 18, 20, 21, 22, 23, 30, 32, 33, 35] [1, 3, 6, 7, 9, 12, 14, 16, 17] [1, 3, 6, 7] #本程序的设计核心是 递归和二分法则:每次取出有序序列中间的那个值和要查找的number比较,大于取序列的右边,小于取出序列的左边,循环递归,直到找出结果; [1, 3] find it [1, 3] None
生成器函数
yeild 把函数的执行步骤,以yield为分界线划分成一个单位 ,遇到next(a)方法返回一个单位的执行结果1次。
def rang2():
star=0
while star<100:
yield star*4
star+=1
# for i in rang2():
# print(i)
#生成器定义在函数里的迭代器有 yield字段
#生成器:可以节省内存,拿一个取一个
def rang():
yield 1
yield 2 #yeild 把函数的执行步骤,以yield为分界线划分成一个单位 ,遇到next(a)方法返回一个单位的执行结果1次。
yield 3
# a=rang()
# print(next(a))
# print(next(a))
# print(next(a))
def test3():
i=0
while i<10:
yield "母鸡下一个鸡蛋%s"%i
i+=1
f=test3()
f2=test3()
# print(next(f2))
# print(next(f2))
# print(next(f))
#计算 1+1+2+3+5+8的和
def test4():
a=1
yield a
b=1
yield b
while True:
c=a+b
yield c
a=b
b=c
sum1=test4()
for i in sum1:
print(i)
高阶函数
接收1个函数作为参数输入,return返回1个函数,满足以上2条件其中一个都叫高阶函数。