美文网首页
2.闭包和装饰器

2.闭包和装饰器

作者: celusing | 来源:发表于2022-10-09 14:42 被阅读0次

闭包:https://zhuanlan.zhihu.com/p/93846887
装饰器:
https://www.liaoxuefeng.com/wiki/1016959663602400/1017451662295584
https://www.cnblogs.com/yaoqingzhuan/p/10628592.html
python参考手册:相关章节

一.预备知识

1.作用域、内嵌函数、生命周期

python中的每个函数都是一个新的作用域,或者理解为命名空间
一般而言,函数中定义的变量、内嵌函数等,其生命周期即所在函数的作用域,当函数执行完毕后,函数内所定义变量、内嵌函数等都应该会消失。而在下一次调用时,又会被重新创建。

2.变量搜索

当在函数中访问一个新的变量时,python会在当前命名空间中寻找该变量是否存在。如果不存在则会从上一级命名空间中搜寻,直到顶层命名空间。比如:


image.png

但是在函数中对一个变量进行定义或者赋值时,python只会在当前命名空间中搜寻该变量,如果不存在,则会创建一个新的变量。如果上一级命名空间中存在同名的变量,那么上一级同名变量会在当前作用于中被覆盖。


image.png

二.闭包

1.定义
将组成函数的语句和这些语句执行环境打包在一起时,得到的对象称为闭包。

2.使用场景
函数在python中是第一类对象。也就是说可以把他们当作参数传递给其他函数放在数据结构中、以及作为函数的返回结果等。(这些场景都会触发闭包)
把函数当作数据处理时,它将隐式地携带与定义该函数的周围环境相关的信息(该函数所需的所有信息)。

3.举例说明

  • 示例1:闭包影响自由变量的绑定方式
# foo.py
x = 42
def callf(func):
  return func()

# 调用代码
import foo
x = 37
def helloworld():
  return "Hello world, x is %d" %x
fool.callf(helloworld)

输出结果:
Hello world, x is 37
  • 示例2:闭包捕捉函数执行所需的整个环境


    image.png

    从作用域的角度:foo实际上是调用了内嵌函数inner(), 当执行到inner中的print(x)语句时,在inner()中没有搜寻到x,然后会在outer()命名空间中搜寻,找到x后进行打印。
    从生命周期的角度,foo的值为outer函数的返回值,当执行foo()时,outer函数已经执行完毕了,此时其作用于内定义的变量x也应该已经销毁,因此执行foo()时,当执行到print(x)时,应该会出错。但实际上并没有。
    这其实是python支持函数闭包的特性。

三.装饰器

闭包会捕捉内部的环境信息,因此还可以用于包装现有函数。

1.定义

装饰器是一个函数(也可以是类或者对象),其主要用途是包装另一个函数或者。这种包装的首要目的是光明睁大的修改或者增强被包装对象的行为。

2.分类

1.函数装饰器

根据装饰器本身是否接受参数,可以分为:带参数的装饰器和不带参数的装饰器
A.不带参数的装饰器:包装的函数func本身,作为装饰器的入参。

@trace
def square(x):
  return x*x

#其等价于:
def square(x):
  return x*x
square = trace(square)

# trace代码的实现如下
# 备注:内部函数callf作为返回值,是一个闭包
def trace(func):
  def callf(*args, **kwargs):
    debug_log.write('calling args:%s' % args)
    r = func(*args, **kwargs)
    debug_log.write('%s returned' % r)
    return r
  return callf

B.带参数的装饰器(多一层对装饰器参数的处理)
首先:参数param作为装饰器的入参,返回第一层函数:W。(第一层:对装饰器本身参数的处理)
然后:将包装的函数func本身,作为W的入参,传递执行。(第二层:对包装函数的处理,不带参数的装饰器,只有该层)

# 装饰器带参数的版本:
def log(text):
    def decorator(func):
        def wrapper(*args, **kw):
            print('%s %s():' % (text, func.__name__))
            return func(*args, **kw)
        return wrapper
    return decorator

# 其含义是:
now = log('execute')(now)

# 装饰器本身不带参数的版本:
def log(func):
    def wrapper(*args, **kw):
        print('call %s():' % func.__name__)
        return func(*args, **kw)
    return wrapper
# 其含义是:
now = log(now)
2.使用类作为装饰器

类也可以作为装饰器。


image.png
3.使用对象作为装饰器

根据装饰器的语法,对象也可以作为装饰器。使用对象装饰器有时候会更加灵活,例如能够方便的定制和添加参数。


image.png
4.装饰器装饰类

接受类作为输入,并返回类作为输出


image.png

备注:
由此可见,@本身是一个python的语法糖。它只是按照固定格式进行展开,展开后只要符合python语法,不论是:类、对象、函数等,都允许灵活的搭配使用。

相关文章

网友评论

      本文标题:2.闭包和装饰器

      本文链接:https://www.haomeiwen.com/subject/dqcoartx.html