一个函数套着另一个函数,内部函数又用到外部函数的变量 ,Python中,闭包的主要用途就是用于装饰器的实现
参考博文:https://blog.csdn.net/u013380694/article/details/90019571?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase
1、概念
闭包函数:声明在一个函数中的函数,叫做闭包函数。
闭包:内部函数总是可以访问其所在的外部函数中声明的参数和变量,即使在其外部函数被返回(寿命终结)了之后。
2、特点
让外部访问函数内部变量成为可能;
局部变量会常驻在内存中;
可以避免使用全局变量,防止全局变量污染;
会造成内存泄漏(有一块内存空间被长期占用,而不被释放)
return xxx 是返回定位
return xxx() 是调用
#coding:utf-8
# y = kx + b
# 第一种
k = 1
b = 2
x = 4
print (k*x + b)
print('-'*50 + '第一种')
# 第二种
def line_2(k, x, b):
print(k*x + b)
line_2(1,2,0)
line_2(2,3,0)
line_2(3,2,0)
# 缺点,多次计算这条线上的y值,每次都需要传递k, x, b的值,麻烦
print('-'*50 + '第二种')
# 第三种:全局变量
k = 1
b = 2
def line_3(x):
print(k*x + b)
line_3(1)
line_3(2)
line_3(3)
k = 11
b = 33
line_3(1)
line_3(2)
line_3(3)
# 缺点:如果计算多条线上的y值,需要对全局变量进行修改,代码量增多,麻烦
print('-'*50 + '第三种')
# 第四种:缺省参数
def line_4(x, k=1, b=2):
print(k*x + b)
line_4(1)
line_4(2)
line_4(3)
line_4(1, k=2, b=3)
line_4(2, k=2, b=3)
line_4(3, k=2, b=3)
# 优点:比全局变量方式好,k,b是函数line_4的一部分,而不是全局变量,因为全局变量可以任意的被其他函数修改
# 缺点:如果计算多条线上的y值,需要对关键字参数进行修改,代码量增多,麻烦
print('-'*50 + '第四种')
# 第五种:实例对象
class Line5(object):
def __init__(self, k, b):
self.k = k
self.b = b
def __call__(self, x):
print(self.k*x + self.b)
line_5_1 = Line5(1, 2) # 创建对象1
# 对象.方法()
# 对象() __call__
line_5_1(0)
line_5_1(1)
line_5_1(2)
line_5_2 = Line5(11, 22) # 创建对象2
line_5_2(0)
line_5_2(1)
line_5_2(2)
# 缺点:为了计算多条线上的y值,所有需要保存多个k,b的值,因此用了很多的实例对象,浪费资源
print('-'*50 + '第五种')
# 第六种:闭包
def line_6(k, b):
def create_y(x):
print(k*x + b)
return create_y
line_6_1 = line_6(1, 2)
line_6_1(0)
line_6_1(1)
line_6_1(2)
line_6_2 = line_6(11, 22)
line_6_2(0)
line_6_2(1)
line_6_2(2)
print('-'*50 + '第六种')
函数名仅仅是个变量名,只不过指向了定义函数而已,要么整体之前要么整体之后控制
通用装饰器:①*args,**kwargs ②拆包 ③return
def foo():
print('foo普通函数')
# foo 表示是函数
foo() # 表示执行foo函数
print('*'*50)
def foo():
print('foo匿名函数')
foo = lambda x: x + 1
foo(1) # 执行lambda表达式,而不再是原来的foo函数,因为foo这个名字被重新指向了另一个匿名函数
代码规范:开放、封闭
def set_func(func):
def call_func():
print('----这是权限验证1----')
print('----这是权限验证2----')
func()
return call_func
@set_func # 添加装饰器,优先执行
def test1():
print('----test1----')
test1()
原理: 把一个函数的引用,当成实参传到另一个闭包里面func,在闭包外面的变量,什么时候调用闭包里面的函数什么时候就去调这个引用
def set_func(func):
def call_func():
print('----这是权限验证1----')
print('----这是权限验证2----')
func() # 执行test1函数
return call_func
# @set_func # 添加装饰器,优先执行
def test1():
print('----test1----')
test1 = set_func(test1) # 第二个test1是func()指向了test1函数
test1()
#test1()
demo:统计调用函数时长
import time
def set_func(func):
def call_func():
start_time = time.time()
func() # 执行test1函数
stop_time = time.time()
print('alltimeis %f' % (stop_time -start_time))
return call_func
@set_func # 添加装饰器,优先执行
def test1():
print('----test1----')
for i in range(100000):
pass
test1()
对有参数num无返回值的函数进行装饰
import time
def set_func(func): # func = 传整个函数
print('----开始进行装饰----')
def call_func(num):
print('----这是权限验证1----')
print('----这是权限验证2----')
func(num) # 执行test1函数
return call_func
@set_func # 添加装饰器,优先执行,相当于test1 = set_func(test1)
def test1(num):
print('----test1----%s' % num)
@set_func # 添加装饰器,优先执行,相当于test2 = set_func(test2)
def test2(num):
print('----test2----%s' % num)
# test1 = set_func(test1) 等于装饰器效果
test1(100)
test2(200)
对不定长函数参数进行装饰
def set_func(func): # func = 传整个函数
def call_func(*args, **kwargs): # * 表示可以保存多个
print('----这是权限验证1----')
print('----这是权限验证2----')
# func(args, kwargs) 不行,相当于传递了2个参数:1个元组,1个字典
func(*args, **kwargs) # args:表示元组 *args:表示拆包(*对元组拆,**对字典拆)
return call_func
@set_func # 添加装饰器,优先执行,相当于test1 = set_func(test1)
def test1(num, *args, **kwargs): #*args:参数打包成tuple给函数体调用 **kwargs 打包关键字参数成dict给函数体调用
print('----test1----%d' % num)
print('----test1----', args) # 元组,分别打印
print('----test1----', kwargs) # 元组,分别打印
# test1 = set_func(test1) 等于装饰器效果
test1(100)
test1(100, 200)
test1(100, 200, 300, mm=200)
----这是权限验证1----
----这是权限验证2----
----test1----100
----test1---- ()
----test1---- {}
----这是权限验证1----
----这是权限验证2----
----test1----100
----test1---- (200,)
----test1---- {}
----这是权限验证1----
----这是权限验证2----
----test1----100
----test1---- (200, 300)
----test1---- {'mm': 200}
对有返回值的参数进行装饰:通用装饰器
def set_func(func): # func = 传整个函数
def call_func(*args, **kwargs): # * 表示可以保存多个
print('----这是权限验证1----')
print('----这是权限验证2----')
# func(args, kwargs) 不行,相当于传递了2个参数:1个元组,1个字典
return func(*args, **kwargs) # 返回值接收
return call_func # 返回位置
@set_func # 添加装饰器,优先执行,相当于test1 = set_func(test1)
def test1(num, *args, **kwargs): #*args:参数打包成tuple给函数体调用 **kwargs 打包关键字参数成dict给函数体调用
print('----test1----%d' % num)
print('----test1----', args) # 元组,分别打印
print('----test1----', kwargs) # 元组,分别打印
return 'ok' # 返回值
# test1 = set_func(test1) 等于装饰器效果
ret = test1(100)
print(ret)
多个装饰器对一个函数:装饰器执行顺序从下往上
def add_grant(func): # func = 传整个函数
print('----装饰器1----开始grant----')
def call_func(*args, **kwargs):
print('----这是权限验证1----')
return func(*args, **kwargs) # 拆包
return call_func
def add_select(func): # func = 传整个函数
print('----装饰器2----开始select----')
def call_func(*args, **kwargs):
print('----这是开始选择2----')
return func(*args, **kwargs) # 拆包
return call_func
@add_grant # 装饰器下面没有函数,不执行闭包里面的函数
@add_select
def test1():
print('----test1----')
test1()
----装饰器2----开始select----
----装饰器1----开始grant----
----这是权限验证1----
----这是开始选择2----
----test1----
def set_func_2(func):
def call_func():
return '' + func() + ' '
return call_func
def set_func_1(func):
def call_func():
return '' + func() + '
'
return call_func
@set_func_1
@set_func_2
def get_str():
return 'hahaha'
print(get_str())
类装饰器
class Test(object):
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
print('这里是装饰器添加的功能....')
return self.func()
@Test # get_str = Test(get_str)
def get_str():
return 'hahaha'
print(get_str())
带参数的装饰器
def set_level(level_num):
def set_func(func):
def call_func(*args, **kwargs):
if level_num == 10:
print('----权限级别10,验证----')
elif level_num == 2:
print('----权限级别2,验证----')
return func()
return call_func
return set_func
# ①.调用set_level并且将1当做实参传递
# ②.用①的返回值当做装饰器对test1函数进行装饰
@set_level(10) # 带参数的装饰器
def test1():
print('----test1----')
return 'ok'
@set_level(2)
def test2():
print('----test2----')
return 'ok'
test1()
test2()
----权限级别10,验证----
----test1----
----权限级别2,验证----
----test2----