嵌套函数简单的理解可以看作是在函数的内部再定义函数,实现函数的“私有”。
特点:
函数内部可以再次定义函数。
只有被调用时才会执行(外部函数被调用后,被嵌套函数调用才有效)。
在函数内部,可以调用其他函数。如果一个函数在内部调用自身本身,这个函数就是递归函数。
特点:
- 必须要有明确的终止条件
- 每进入更深一层递归时,问题规模相比上次都应有所减少
- 递归效率不高,层次过多会导致栈溢出
案例解析
def recur_fibo(n):
"""递归函数
输出斐波那契数列"""
if n <= 1:
return n
else:
return(recur_fibo(n-1) + recur_fibo(n-2))
def fib(n):
a, b = 1, 1
for i in range(n-1):
a, b = b, a+b
return a
在一些语言中,在函数中可以(嵌套)定义另一个函数时,如果内部的函数引用了外部的函数的变量,则可能产生闭包。闭包可以用来在一个函数与一组“私有”变量之间创建关联关系。在给定函数被多次调用的过程中,这些私有变量能够保持其持久性。
简而言之, 闭包的特点就是内部函数引用了外部函数中的变量。
在Python中,支持将函数当做对象使用,也就是可以将一个函数当做普通变量一样用作另一个函数的参数和返回值。拥有此类特性的语言,一般都支持闭包。
闭包中被内部函数引用的变量,不会因为外部函数结束而被释放掉,而是一直存在内存中,知道内部函数被调用结束。
也可以说是外部函数通过return将内部函数抛出
闭包的三个条件:
- 在外部函数中定义内部函数
- 外部函数有返回值
- 返回值就是内部函数名
- 内部函数引用外部函数的变量
应用
def func(a, b):
c = 10
def inner_func():
s = a + b + c
print("相加后的结果", s)
return inner_func # 不能加括号,加括号表示调用
#其实ifunc是间接调用了inner_func,虽然不能直接调用
ifunc=func(6,9)
ifunc1=func(2,8)
ifunc2=func(1,9)
print(ifunc)
print(ifunc1)
print(ifunc2)
ifunc2()
ifunc1()
ifunc()
由上述实验可以看出,每次调用内部函数结束后,内存都不会释放,调用几次就会开辟几个内存空间,ifunc函数的地址都不一样就已经很好的说明了,而函数的返回结果则都是根据自己的内存空间中记录的数据而计算出来的
闭包的缺点
- 作用域没有那么直观 闭包的缺点
- 因为变量不会被垃圾回收所以有一定的内存 占用问题。
闭包作用:
- 可以使用同级的作用域 闭包作用:
- 读取其他元素的内部变量 闭包作用:
- 延长作用域
闭包总结
- 闭包似优化J变量,原来需要类对象完成的工作,闭包也可以完成
- 由于闭包引用J外部函数的局部变量,则外部函数的局部变量没有及时释放,消耗内存
- 闭包的好处,使代码变得简洁,便于阅读代码。
- 闭包是理解装饰器的基础
python装饰器(fuctional decorators)就是用于拓展原来函数功能的一种函数,目的是在不改变原函数名(或类名)的情况下,给函数增加新的功能。
这个函数的特殊之处在于它的返回值也是一个函数,这个函数是内嵌“原“”函数的函数。
#定义一个装饰器
def decorate(func):
a = 100
print('wrapper外层打印测试')
def wrapper(*args, **kwargs):
func(*args, **kwargs)
print('--------->刷漆')
print('--------->铺地板', a)
print('------->装门')
print('wrapper加载完成..')
return wrapper
#定义一个函数
@decorate
def func():
print("我是毛坯房")
print(func)
func()
@decorate
代码会加载执行decorate函数,所以会先输出 'wrapper外层打印测试’- decorate装饰器将func当做参数传入函数体中 ,定义内部函数,并返回内部函数的地址,这样,func函数就替换成wrapper函数,也就是上图的第三行,实现了添加功能的目的
- 调用func函数时,实际上调用的是wrapper函数,所以先输出‘我是毛坯房’,而‘刷漆’是最后输出的
当被装饰的函数带有参数时
#定义一个装饰器
def decorate(func):
a = 100
print('wrapper外层打印测试')
def wrapper(*args, **kwargs):
func(*args, **kwargs)
print('--------->刷漆')
print('--------->铺地板', a)
print('------->装门')
print('wrapper加载完成..')
return wrapper
#定义一个函数
@decorate
def func(n, m):
print("我是毛坯房,需要{}人,{}砖".format(n, m))
print(func)
func(5, 10)
*args,**kwargs
,代表可以接受所有类型的参数
给目标函数添加多个功能
def zhuang1(func):
print('------>1 start')
def wrapper(*argx, **kwargs):
func()
print('刷漆')
print('------->1 end')
return wrapper
def zhuang2(func):
print('------>2 start')
def wrapper(*argx, **kwargs):
func()
print('铺地板,装门.....')
print('------->2end')
return wrapper
@zhuang2
@zhuang1
def func():
print("我是毛坯房")
func()
- 那个装饰器离函数最近,就优先执行那个装饰器
- 多个装饰器使用,函数都会保留每次装饰后的状态,所以刷漆和铺地板都会输出
装饰器需要传递参数
def outer(a): #第一层:负责接收装饰器的参数
def decorate (func): #第二层:负 责接收函数的
def wrapper (*args, **kwargs): #第三层 负责接收函数的参数
func (*args,**kwargs)
print("---->铺地砖{}块”. format (a))
return wrapper
#返出来的是:第三层
return decorate #返出来的是:第二层
@outer (10)
def house (time) :
print('我{}日期拿到房子的钥匙,是毛坯房....’. format (time))
- 最外层装饰器的作用就是接受内层装饰去的参数
装饰器实现一个函数计时器
1.被装饰的函数如果有 返回值的时候 怎么办?
2.如何保留被装饰函数的函数名字和帮助文档信息
import functools
import random
import string
import time
li = [random.choice(string.ascii_letters) for i in range(1000000)]
def timeit(fun):
# @functools.wraps(fun)
def wrapper(*args, **kwargs): # 接收可变参数和关键字参数
"""这是一个装饰器timeit"""
# 在函数执行之前
start_time = time.time()
# 执行函数
res = fun(*args, **kwargs)
# 在函数执行之后
stop_time = time.time()
print('运行事件为:%.6f' % (stop_time - start_time))
return res
return wrapper
#计算两种添加字符串的时间
@timeit
def con_add():
s = ''
for i in li:
s += (i + '')
print('0')
@timeit
def join_add():
s = ','.join(li)
print('0')
con_add()
join_add()
print(con_add.__name__)
print(con_add.__doc__)
打印日志信息
def add_log(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
start_time = time.time()
res = func(*args, **kwargs)
end_time = time.time()
print('[%s] 函数名:%s,运行时间:%.6f,运行返回值的结果'
':%d' % (time.ctime(), func.__name__,
end_time - start_time, res))
return res
return wrapper
@add_log
def add(x, y):
time.sleep(1)
return x + y
add(1, 10)
编写装饰器required_ints, 条件如下:
- 确保函数接收到的每一个参数都是整数;
- 如果参数不是整形数, 打印 TypeError:参数必须为整形
import functools
def required_ints(func):
@functools.wraps(func)
def wrapper(*args, **kwargs): # args=(1,2,..)
for i in args:
if isinstance(i, int):
print('函数所有的参数并非都是int型')
break
else:
res = func(*args, **kwargs)
return res
return wrapper
@required_ints
def add(a, b):
return a + b
@required_ints
def mymax(a, b, c, d):
return max(a, b, c, d)
print(add(1, 2.0))
下例定义两个装饰器a,b,通过显示的结果可以了解装饰器的运行顺序
def decorator_a(fun):
print('a')
def inner_a(*args, **kwargs):
print('Get in inner_a')
fun(*args, **kwargs)
return inner_a
def decorator_b(fun):
print('b')
def inner_b(*args, **kwargs):
print('Get in inner_b')
fun(*args, **kwargs)
return inner_b
@decorator_a
@decorator_b
def f(x):
print('Gat in f')
# f(1)
f = decorator_b(f)
根据显示的结果,装饰器的运行顺序就像穿衣服和脱衣服,穿和脱的顺序是相反的
多个装饰器的应用场景: 会采用多个装饰器先验证是否登陆成功 再验证权限是否足够
import functools
import inspect
def is_login(fun):
@functools.wraps(fun)
def wapper(*args, **kwargs): # ('admin',)
if args[0] in login_session:
temp = fun(*args, **kwargs)
return temp
else:
print('Error:%s 没有登陆成功' % (args[0]))
return wapper
def is_admin(fun):
@functools.wraps(fun)
# nspect.getcallargs会返回一个字典
# key:形参 value:对应的实参
def wrapper(*args, **kwargs):
inspect_res = inspect.getcallargs(fun, *args, **kwargs)
print('inspect的返回值是:%s' % (inspect_res))
if inspect_res.get('name') == 'root':
temp = fun(*args, **kwargs)
return temp
else:
print('not root user,no permisson add user')
return wrapper
login_session = ['root', 'admin', 'redhat']
@is_login
@is_admin
def add_user(name):
print('add_user~~')
add_user('root')
常规调用 | 嵌套调用 | 递归调用 | 闭包 | |
---|---|---|---|---|
函数的调用 | 普通函数 | 同级函数 | 自身 | 内部函数 |
参数的引用 | 全局变量或者无参数传入 | 调用函数传给被调函数,或者无参数传递 | 上一层自己传给下一个自己 | 内部函数引用外部函数的变量 |
返回值 | 返回给外界,或者没有返回值 | 返回给调用函数 | 返回运算结果给下一个“自己” | 返回内部函数名给外界 |
内存划分 | 从全局内存中划分 | 从全局内存中划分,函数之间内存空间独立 | 从全局内存中划分,函数之间内存空间独立 | 内部函数内存空间在外部函数中 |