Python闭包与装饰器核心机制及实战解析

远山来客
远山来客
发布时间:2026-10-12 | 4人浏览过
原创

本文深入解析Python中闭包与装饰器的核心机制。详细阐述闭包的三个构成条件,并系统介绍装饰器的定义、执行顺序、带参数实现、基于类的实现方式以及内置装饰器如@property的用法。通过具体代码示例,帮助开发者掌握如何利用装饰器扩展函数功能,提升代码复用性与可维护性。

闭包

Python支持函数的嵌套定义。当内部函数引用了外部函数(非全局作用域)中的变量时,该内部函数即被称为闭包。

要构成闭包,需同时满足以下三个条件:

1. 存在两个嵌套的函数结构,且闭包特指内部函数;

2. 内部函数引用了外部函数的变量(即自由变量);

3. 外部函数将内部函数的名称作为返回值返回。

具体示例如下:

装饰器

1、装饰器本质上是一个Python函数,其作用是在不修改原函数代码的前提下,为其增加额外功能,且装饰器的返回值也是一个函数对象。它常用于处理切面需求,例如插入日志、性能测试、事务处理、缓存机制或权限校验等。装饰器是解决此类问题的优秀设计模式,通过它可以将与核心功能无关的重复代码抽离出来并重用。

2、装饰器作为一个函数,必须接收一个参数,该参数代表被修饰的目标函数。

例如,定义如下一个装饰器函数:

def w1(func):
   print(‘正在装饰')
    def inner():
        print(‘正在验证权限')
        return inner()
  • 装饰器本质上是一个嵌套函数结构。
  • 其中,内部函数构成了一个闭包。
  • 外部函数接收的参数是被修饰的函数(func)。

3、在函数定义前添加@符号及装饰器名称,即可实现对该函数的包装。

为f1函数添加装饰器的示例如下:

@w1
def f1():
    print('f1')

此时,程序在编译阶段会自动生成调用装饰器函数的代码,其效果等价于: f1 = w1(f1)

多个装饰器

当多个装饰器应用于同一个函数时,其执行顺序是从下至上依次进行。

@w1
@w2
def f1():
        print(‘---f1---')

具体的执行顺序为:先执行@w2,后执行@w1

带参数的装饰器

假设前文的装饰器不仅需要记录进入函数时的日志,还需要指定日志的级别,那么装饰器的实现形式将发生变化。

  def logging(level):
        def wrapper(func):
            def inner_wrapper(*args, **kwargs):
                print "[{level}]: enter function {func}()".format(
                    level=level,
                    func=func.__name__)
                return func(*args, **kwargs)
            return inner_wrapper
        return wrapper
    @logging(level='INFO') def say(something):
        print "say {}!".format(something)
    # 如果没有使用@语法,等同于
    # say = logging(level='INFO')(say)
    @logging(level='DEBUG') def do(something):
        print "do {}...".format(something)
    if __name__ == '__main__':
        say('hello')
        do("my work")
  • 当带参数的装饰器应用于某个函数时,例如@logging(level=‘DEBUG’),这实际上是一个立即执行的函数。只要该函数返回的结果是一个有效的装饰器,即可正常工作。

基于类实现的装饰器

装饰器函数遵循特定的接口约束:它必须接受一个callable对象作为参数,并返回一个callable对象。

在Python中,callable对象通常指函数,但也存在例外。只要某个对象重载了__call__()方法,该对象即为callable。

class Test():
    def __call__(self):
        print 'call me!'
t = Test()
t()  # call me

像__call__这样前后带有双下划线的方法,在Python中被称为内置方法,有时也被称作魔法方法。

重载这些魔法方法通常会改变对象的内部行为。上述示例展示了如何让一个类对象具备被调用的行为。

回到装饰器的概念,它要求接受一个callable对象,并返回一个callable对象(此描述较为宽泛,详细情况见后文)。

因此,使用类来实现装饰器也是可行的。我们可以让类的构造函数__init__()接收一个函数,随后重载__call__()方法并返回一个函数,从而达到与普通装饰器函数相同的效果。

class logging(object):
    def __init__(self, func):
        self.func = func
    def __call__(self, *args, **kwargs):
        print "[DEBUG]: enter function {func}()".format(
            func=self.func.__name__)
        return self.func(*args, **kwargs)
@logging
def say(something):
    print "say {}!".format(something)

带参数的类装饰器

若需通过类形式实现带参数的装饰器,实现过程会相对复杂。此时,构造函数接收的不再是函数,而是传入的参数,并通过类将这些参数保存下来。在重载__call__方法时,需接收一个函数并返回一个函数,以此实现装饰器功能。

class logging(object):
    def __init__(self, level='INFO'):
        self.level = level
    def __call__(self, func): # 接受函数
        def wrapper(*args, **kwargs):
            print "[{level}]: enter function {func}()".format(
                level=self.level,
                func=func.__name__)
            func(*args, **kwargs)
        return wrapper  #返回函数
@logging(level='INFO')
def say(something):
    print "say {}!".format(something)

内置的装饰器

内置装饰器与普通装饰器的原理相同,区别在于其返回的是类对象而非函数。在理解此装饰器之前,需了解不使用装饰器时如何定义属性。

def getx(self):
    return self._x
def setx(self, value):
    self._x = value
def delx(self):
   del self._x
# create a property
x = property(getx, setx, delx, "I am doc for x property")

上述代码展示了Python属性的标准写法,其逻辑与Java相似,但代码较为繁琐。使用@语法糖可以实现相同效果,且代码更为简洁。

@property
def x(self): ...
# 等同于
def x(self): ...
x = property(x)

带有返回值的装饰器

  def func(functionName):
        def func_in():  	  
        	return functionName()    
        return func_in 
     @func def test():
        return ‘itheima'

至此,关于Python中闭包与装饰器用法的详解介绍完毕。如需了解更多Python装饰器相关内容,建议搜索本站历史文章或继续阅读后续相关文章,感谢大家的支持!

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表