Python:装饰器与闭包

python中的装饰器真的是个很难理解的概念,以下只是我在学习过程中的一点浅见,学习交流。

1 闭包

在谈装饰器之前,不得不说一下闭包的概念。什么是闭包呢?维基百科上是这么讲的——

在计算机科学中,闭包(英语:Closure),又称词法闭包(Lexical Closure)或函数闭包(function closures),是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。

 简直不像人话。我们通过一小段代码来感受一下闭包:

def foo():
    a = 'free var'
    def bar():
        print(a)
    return bar

代码中有两个函数:外函数foo()以及内函数bar(),这个嵌套函数就形成了一个闭包。闭包是延伸了作用域的函数,它使得内函数bar()能够访问未在其函数体中定义的非全局变量,未在函数定义体中定义的非全局变量一般都是在嵌套函数中出现。

形成闭包一般需要以下条件:

  1. 在一个外函数中定义一个内函数;
  2. 外函数的返回值是内函数的引用;
  3. 内函数里运用了外函数的临时变量。

2 装饰器

装饰器简单来讲是修改其他函数的功能的函数。装饰器也是一个闭包,只不过变量是被装饰的另一个函数。可以在装饰器内部实现额外的功能,以增强被装饰函数的行为,然后返回被装饰的函数或者将其替换成一个新的函数。

2.1 一般装饰器

def wrapper(func):
     def inner(*args,**kwargs):
         # function code
         return func(*args,**kwargs) 
     return inner

装饰器的语法糖@,使用方式:

@wrapper
def test(var):
    pass

 运行的过程:

wrapper(test) ===> inner(var) ===> test(var)

2.2 带参数装饰器

 有时候装饰器本身需要使用一些参数,这时装饰器在定义时需要三层函数。以下是一个例子:

def logit(logfile='out.log'):
    def logging_decorator(func):
        def wrapped_function(*args, **kwargs):
            # function code
            return func(*args, **kwargs)
        return wrapped_function
    return logging_decorator

它实际上是对原有装饰器的一个函数封装,并返回一个装饰器。我们可以将它理解为一个含有参数的闭包。当我们使用@logit(logfile='out.log')调用的时候,Python能够发现这一层的封装,并把参数传递到装饰器的环境中。 

 

2.3 装饰器类

class logit(object):
    def __init__(self, logfile='out.log'):
        self.logfile = logfile
 
    def __call__(self, func):
        def wrapped_function(*args, **kwargs):
            # function code
            return func(*args, **kwargs)
        return wrapped_function
 
    def notify(self):
        pass

 这个实现有一个附加优势,在于比嵌套函数的方式更加整洁,而且包裹一个函数还是使用跟以前一样的语法。

 2.4 类装饰器

装饰器的参数也可以是一个类,也就是说,装饰器可以装饰类:

import types

def deco(cls):
    for key, method in cls.__dict__.items():
        if isinstance(method, types.FunctionType):
            print(key, ':', method.__name__)
    return cls

@deco
class Test:
    def __init__(self):
        pass

    def foo(self):
        pass

2.5 装饰器的其他问题

1.装饰器可以叠加使用,一个被装饰的函数可以使用多个装饰器。

@wrapper1
@wrapper2
def func()
    pass

等效于 f = wrapper1( wrapper2( func ) )

2.装饰器是导入时运行的,而被装饰的函数是明确调用时运行的。

3.自定义的装饰器会改变被装饰函数的元信息,如docstring,__name__等,我们可以使用标准库functools中的wraps函数来解决这个问题。wraps本身也是一个装饰器,它能把原函数的元信息拷贝到装饰器函数中,这使得装饰器函数也有和原函数一样的元信息了。

from functools import wraps

def decorator_name(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        # function code
        return f(*args, **kwargs)
    return decorated

 

 

参考文章:

Python 函数装饰器

如何理解Python装饰器?

Python装饰器与闭包

理解Python装饰器(Decorator)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值