在把函数当作对象的语言(python、javascript)中闭包的概念肯定是少不了的。
闭包的定义在一些语言中,在函数中可以(嵌套)定义另一个函数时,如果内部的函数引用了外部的函数的变量,则可能产生闭包。闭包可以用来在一个函数与一组“私有”变量之间创建关联关系。在给定函数被多次调用的过程中,这些私有变量能够保持其持久性。
实例
defEx(count):
print(count)
defe():
# 内部函数==>用了外部函数的变量
returncount+1
returne
外函数返回了内函数的引用
外函数把临时变量绑定给内函数
修改变量【非常重要】
想一下python中是如何修改全局变量的?
采用global关键词
将全局变量改为可变的数据类型
可变的数据类型:list 、dict set 【复习笔记中有详细说明】
同理python闭包中内函数如何修改?
python3中加入了nonlocal 关键字
采用可变的数据类型【python2 只能采用这种策略了】
使用
defcounter(start_at=):
count=[start_at]
defincr():
count[]+=1
returncount[]
returnincr
一个简单的计数器实现计数功能!
>>>c=counter(10)
>>>c()
11
>>>c()
12
>>>
python3 中的实例
>>>defcounter(s):
defadd():
nonlocals
s+=1
returns
returnadd
>>>c=counter(10)
>>>c()
11
>>>c()
12
闭包的作用
装饰器是不是很像呢?
面向对象属性和方法
单例模式没用过(难受)
闭包的优缺点
优点:提高代码的复用性
缺点:
由于闭包引用了外部函数的局部变量,外部函数的局部变量没及时释放,消耗内存
闭包类似于实例对象一样,是一个独立空间,多个闭包就是多个各自独立的空间,互不影响
闭包占用的内存空间要远小于一个实例对象的空间,因此在需要一个独立空间(需要变量+功能)时,除了考虑实例对象之外,还可以考虑闭包
关于闭包的补充
一切都是对象来说,闭包也有个不得不说的属性即:closure属性定义的cell对象
c=counter()
c()
print(c.__closure__[].cell_contents)
c()
print(c.__closure__[].cell_contents)
# 输出 1 2
装饰器
在有了前面闭包的理解,下面的装饰器用一个实例引入,比如说我要添加一个自动计数的功能该如何实现?
用闭包实现
defcounter(start_at=):
count=start_at
defincr():
nonlocalcount
count+=1
returncount
returnincr
c=counter()
defoutput():
'''
一堆输出操作
'''
print(" %d times"%c())
importtime
whileTrue:
output()
time.sleep(2)
但是,再用闭包实现时,要改动output()函数中的代码哈,如果不想改变output中的代码该怎么办呢?这就用到装饰器了!
装饰器定义
在不修改原函数的代码的情况下,添加新的功能。可以在执行原函数之前加,也可也在执行原函数之后添加
装饰器用法
defcounter(func,count=):
defwrapper():
func()
nonlocalcount
count+=1
print(" %d times"%count)
returnwrapper
importtime
@counter
defoutput():
time.sleep(2)
whileTrue:
output()
跟闭包差不多,却把函数当参数传递进去了!这就是无参函数的基本用法
函数带参数
# -*- coding: utf-8 -*-
fromtimeimportctime
fromtimeimportsleep
defftfunc(func):
deftimef(*s,**gs):
print"[%s] %s() called"%(ctime(),func.__name__)
returnfunc(*s,**gs)
returntimef
@ftfunc
deffoo(*s,**gs):
print(s)
print(gs)
if__name__=='__main__':
foo()
foo(1)
foo(1,2)
foo(1,2,3)
stu= {'name':'alam','age':12}
foo(1,2,3,**stu)
'''
运行结果
[Wed Jun 14 15:49:55 2017] foo() called
()
{}
[Wed Jun 14 15:49:55 2017] foo() called
(1,)
{}
[Wed Jun 14 15:49:55 2017] foo() called
(1, 2)
{}
[Wed Jun 14 15:49:55 2017] foo() called
(1, 2, 3)
{}
[Wed Jun 14 15:49:55 2017] foo() called
(1, 2, 3)
{'age': 12, 'name': 'alam'}
[Finished in 0.1s]
'''
装饰器带参数
# -*- coding: utf-8 -*-
defdecrator(*dargs,**dkargs):
defwrapper(func):
def_wrapper(*args,**kargs):
print"decrator param:",dargs,dkargs
print"function param:",args,kargs
returnfunc(*args,**kargs)
return_wrapper
returnwrapper
@decrator(1,a=2)
deffoo(x,y=):
print"foo",x,y
if__name__=='__main__':
foo(3,4)
'''
执行结果
decrator param: (1,) {'a': 2}
function param: (3, 4) {}
foo 3 4
[Finished in 0.1s]
'''
带返回值的装饰器
defcounter(func,count=):
defwrapper():
func()
nonlocalcount
count+=1
print(" %d times"%count)
returncount
returnwrapper
importtime
@counter
defoutput():
time.sleep(2)
whileTrue:
a=output()
print(a)
多装饰器
importlogging
defcounter(func,count=):
defwrapper():
func()
nonlocalcount
count+=1
print(" %d times"%count)
returncount
returnwrapper
deflog(func):
defwrapper():
logging.warning("warning test %s"%func.__name__)
return
returnwrapper
importtime
@log
@counter
defoutput():
time.sleep(2)
whileTrue:
a=output()
print(a)
time.sleep(5)
装饰器是递归输出的!同时可判断! 先输出log 在输出times 返回值是先用那个装饰器就返回谁的
领取专属 10元无门槛券
私享最新 技术干货