Python中的闭包和装饰器

1、闭包(closure)

闭包在很多函数式编程语言中都会支持,主要的意义是的内部函数和局部变量可以在全局中进行访问。其主要关注的是变量作用域的问题。Python作为一种面向对象的语言,类的存在完全实现了这种功能,但是它也支持闭包操作,在一些比较简单的操作中类的实现相比闭包来说要复杂很多,同时装饰器也是属于闭包的一种应用。

闭包的定义主要有2种:

从形式来看,如果在一个内部函数里对外部作用域变量(非全局变量)的引用,那么这种行为被称为是闭包(closure)。定义在外部函数内但是在内部函数引用或使用的变量称为自由变量。

def incr(init):
    a = init
    def inner():
        nonlocal a
        a += 1
        print(a)
    return inner
a1 = incr(5)
a2 = incr(5)
a1()
a1()
a1()
a2()
-------------------
6
7
8
6

以上是一个闭包的简单示例,incr函数调用返回内层函数的可执行对象,即a1 = incr(5),返回inner,后面a1()的使用实际上相当于inner()的调用,即调用内层函数对自由变量a的加1计算。 内部函数可以访问外部函数或者全局变量,但是一般情况下不能直接修改,对于全局变量来说需要加global关键字;而对于外部函数变量需要加nonlocal变量。

从实现意义来看,内部函数可以访问外部函数变量的特点很像将外部函数的变量直接“打包”到内部函数中一样,因此:将组成函数的语句以及执行这些语句的环境“打包”在一起时得到的对象称为闭包,它是作为一个整体出现在程序中的,会占用一定的内存空间。以上示例中,在调用函数得到a1/a2后,可以看出是两份独立的结果,并且a1在多次调用时,可以看出闭包像一个整体将自由变量的值保存了下来,这样其实就实现了局部变量可以在全局变量的访问。若声明一个类也可以具有相同的功能。

2、装饰器(decorator)

Python中装饰器是闭包的一个应用,主要用于对某些函数添加某些功能而又不希望破坏原代码,或者给多个函数添加某一个功能,比如记录执行时间,这样写一个装饰器可以用于多个函数而不必逐个修改。

from time import time,sleep
def outer(func):
    def inner():
       start = time()
       func()
       print("run time: %d"%(time()-start))
    return inner

@outer
def test():
    print("test start....")
    sleep(5)
    print("test end....")
test()
-----------------------------------------
test start....
test end....
run time: 5

函数test需要添加一个统计执行时间的功能,但是又不想在test函数内部去做修改。装饰器提供这样的功能,outer表示一个装饰器,它的参数是一个函数对象,其内层函数inner中调用了传入的参数,实际上本例中test就是一个自由变量。在参数func调用完成后,计算执行时间。而形式上就是在被装饰的函数前面使用@+装饰器,@outer就表示使用outer(test),执行返回inner,而test()就表示对返回的inner进行调用,即inner(),于是实现了函数功能的添加。

python也支持多个装饰器的使用,即在函数上方依次使用@+装饰器,执行时从距离函数最近的装饰器开始依次执行。并且装饰器也支持有参数的函数使用。

from time import time,sleep
def outer1(func):
    def inner(*args, **kwargs):
       start = time()
       func(*args, **kwargs)
       print("run time: %d"%(time()-start))
    return inner

def outer2(func):
    def inner(*args, **kwargs):
       func(*args, **kwargs)
       print("func args is %s"% (args))
    return inner

@outer2
@outer1
def test(time):
    print("test start....")
    sleep(time)
    print("test end....")

test(3)
------------------------------------------
test start....
test end....
run time: 3
func args is 3

以上除添加执行时间(outer1)功能外,还需要将参数打印(outer2)。参数的传入放在内部函数中,即使用inner(*args, **kwargs),因为装饰器返回的是内部的函数,所以参数要加到内部函数参数中。其中*args表示无关键字参数顺序传入、**kwargs表示关键字参数,类似于字典类参数,关于参数的问题暂不讨论。由执行结果可以看出,装饰器依次执行,先执行outer1后执行outer2,相当于outer2(outer1(test))()。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值