python中闭包,装饰器

本文详细探讨了Python中的闭包和装饰器概念。闭包是指在内部函数中引用外部函数的局部变量,即使外部函数执行完毕,这些变量仍然保留。装饰器则是一种特殊类型的闭包,用于在不修改函数源代码的情况下增加其功能,如日志、性能测试等。文中通过实例展示了装饰器的使用,包括无参数、带参数、多层和类装饰器的实现,强调了装饰器提高代码复用性和可读性的优势。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

闭包

在一个外函数中定义了一个内函数,内函数里运用了外函数的临时变量,并且外函数的返回值是内函数的引用。这样就构成了一个闭包;

一般情况下,在我们认知当中,如果一个函数结束,函数的内部所有东西都会释放掉,还给内存,局部变量都会消失。但是闭包是一种特殊情况,如果外函数在结束的时候发现有自己的临时变量将来会在内部函数中用到,就把这个临时变量绑定给了内部函数,然后自己再结束。

定义:

如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure);

闭包=内部函数+定义函数时的环境

举例一:

def outer():
    x = 10
    def inner():#条件一 inner就是内部函数
        print(x)#条件二 外部环境的一个变量
    return inner#结论:内部函数inner就是一个闭包
#outer()()
f = outer()     #这两步相当于outer()()
f()

执行结果:

10

举例二:

def outer(x):   #outer是外部函数,x是外部函数的临时变量;
    def inner(y):   #inner是内函数
        nonlocal x  #内函数想修改闭包变量,nonlocal关键字声明我要修改X;
        x += y      #x = x+y
        return x    #结束返回X的值;
    return inner    #外函数的返回值是内函数的引用;

a = outer(5)
print(a(5))
print(a(10))

执行结果:由此可见,每次调用inner的时候,使用的闭包变量x实际上是同一个。

10
20

装饰器

装饰器(Decorators)是 Python 的一个重要部分,本质上也是一个python函数。简单地说:他们是修改其他函数的功能的函数。他们有助于让我们的代码更简短,也更Pythonic(Python范儿),可以让其他函数在不需要做任何代码变动的情况下增加额外功能,装饰器的返回值也是一个函数对象。比如:插入日志、性能测试、事务处理、缓存、权限校验等应用场景。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能。

例如简单装饰器,计算代码执行时间:

import time
def show_time(x):
    def inner():
       start = time.time()
       x()
       end = time.time()
       print('spend %s'%(end - start))
    return inner

def bar():
    print('bar....')
    time.sleep(1)    #为了模拟实验环境,让他这里等待一秒;
bar = show_time(bar) 
bar()

@show_time #相当于foo = show_time(foo);
def foo():
    print('foo.....')
    time.sleep(2)
foo()

@show_time  #test = show_time(test);
def test():
    print('test.....')
    time.sleep(3)
test()

@符号是装饰器的语法糖,在定义函数的时候使用,避免再一次赋值操作;

这样我们就可以省去foo = show_time(foo)这一句了,直接调用foo()即可得到想要的结果。如果我们有其他的类似函数,我们可以继续调用装饰器来修饰函数,而不用重复修改函数或者增加新的封装。这样,我们就提高了程序的可重复利用性,并增加了程序的可读性。

foo=show_time(foo)其实是把inner引用的对象引用给了foo,而inner里的变量func之所以可以用,就是因为inner是一个闭包函数。

执行结果:

bar....
spend 1.0014598369598389
foo.....
spend 2.0005860328674316
test.....
spend 3.000148296356201

带参数的被装饰函数

import time
def show_time(f):
    def inner(x,y):
        start_time = time.time()
        f(x,y)
        end_time = time.time()
        print('spend %s'%(end_time - start_time))
    return inner

@show_time
def add(a,b):
    print(a + b)
    time.sleep(2)
add(2,3)

执行结果:

5
spend 2.000265121459961

不定长参数

import time
def show_time(f):
    def inner(*x,**y):
        start_time = time.time()
        f(*x,**y)
        end_time = time.time()
        print('spend %s'%(end_time - start_time))
    return inner

@show_time
def add(*x,**y):
    sum = 0
    for i in x:
        sum += i
    print(sum)
    time.sleep(2)
add(2,3,4,5,6)

执行结果:

20
spend 2.000115394592285

带参数的装饰器

装饰器还有更大的灵活性,例如带参数的装饰器:在上面的装饰器调用中,比如@show_time,该装饰器唯一的参数就是执行业务的函数。装饰器的语法允许我们在调用时,提供其它参数,比如@decorator(x)。这样,就为装饰器的编写和使用提供了更大的灵活性;

import time
def logger(flag = ''):
    def show_time(f):
        def inner(*x,**y):
            start_time = time.time()
            f(*x,**y)
            end_time = time.time()
            print('spend %s'%(end_time - start_time))
            if flag == 'true':
                print('这个操作时间将记录到日志中')
        return inner
    return show_time

@logger('true')
def add(*x,**y):
    sums = 0
    for i in x:
        sums += i
    print(sums)
    time.sleep(2)
add(2,3,4,5,6)

@logger()
def foo():
    print('foo...')
    time.sleep(2)
foo()

结果如下:(需要记录到日志中的就加正确参数,不需要的就不用加参数)

20
spend 2.0003697872161865
这个操作时间将记录到日志中

foo...
spend 2.0006625652313232

logger是允许带参数的装饰器。它实际上是对原有装饰器的一个函数封装,并返回一个装饰器(一个含有参数的闭包函数)。当我 们使用@logger(‘true’)调用的时候,Python能够发现这一层的封装,并把参数传递到装饰器的环境中。

多层装饰器

def makebold(fn):
    def wrapper():
        return '<b>' + fn() + '</b>'
    return wrapper

def makeitalic(fn):
    def wrapper():
        return '<i>' + fn() + '</i>'
    return wrapper

@makebold
@makeitalic
def hello():
    return "hello world!"
# hello()
print(hello())

执行结果:

<b><i>hello world!</i></b>

类装饰器

再来看看类装饰器,相比函数装饰器,类装饰器具有灵活度大、高内聚、封装性等优点。使用类装饰器还可以依靠类内部的__call__方法,当使用 @ 形式将装饰器附加到函数上时,就会调用此方法。

import time
class like(object):
    def __init__(self, func):
        self._func = func
        
    def __call__(self):
        start_time = time.time()
        self._func()
        end_time = time.time()
        print('spend %s' % (end_time - start_time))
        
@like
def foo():
    print('foo....')
    time.sleep(2)
foo()

执行结果:

foo....
spend 2.0000457763671875

 

https://stackoverflow.com/questions/739654/how-to-make-a-chain-of-function-decorators/1594484#1594484

https://www.cnblogs.com/yuanchenqi/articles/5830025.html

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值