美文网首页
Python闭包与装饰器

Python闭包与装饰器

作者: 马本不想再等了 | 来源:发表于2019-02-12 23:49 被阅读0次

1. 闭包

概念:在函数嵌套的前提下,内层函数引用了外层函数的变量(包括参数),外层函数又把内层函数当做返回值进行返回。这个内层函数+多引用的外层变量,称为‘闭包’。
实例1:

def test1(a):
  b=10
  def test2()
    print(a)
    print(b)
  return test2

应用场景:外层函数,根据不同的参数生成不同作用功能的函数。
注意事项:

  1. 闭包中,如果要修改引用的外层变量,则需要使用nonlocal变量声明,否则会当做是闭包内,新定义的变量。
  2. 当闭包内,引用了一个后期会发生变化的变量时,一定要注意。
    实例2:
def test()
  funcs = []
  for i in range(1, 4):
    def test2():
      print(i)
    funcs.append(test2)
  return funcs
newfuncs = test()

newfuncs[0]()
newfuncs[1]()
newfuncs[2]()

>>>3
>>>3
>>>3

以上情况的出现是因为,虽然在最后newfuncs中调用不同的test2函数,但在此之前funcs函数内的test2没有被执行,而test2函数已经被定义了3次了,其中range(1, 4)也已经走了3次了,相应的i也已经从1走到3了,所以在最后调用test2的,会去取i的值,此时i=3,故三次调用都会返回3。
案例2改进:

def test():
  funcs = []
  for i in range(1, 4):
    def test2(num):
      def inner():
        print(num)
      return inner
    funcs.append(test2=(i))
  return funcs
newfuncs = test()

newfuncs[0]()
newfuncs[1]()
newfuncs[2]()

>>>1
>>>2
>>>3

解释:在改进方案中,其实就是给之前的test2内再嵌套了一个函数inner,inner负责之前test2的功能,然后每次test2加入funcs列表的时,直接把的i传入test2,同时又不执行inner函数,之后newfuncs调用test2时,其中已经包含的每次不同的i的值,inner函数执行输出的结果也就会不同。

2. 装饰器

2.1 概念理解

装饰器本质上是一个函数,它可以让其他函数在不需要做任何改动的前提下增加额外的功能,装饰器的返回值也是一个函数对象。
装饰器经常用于有切面需求的场景:插入日志、性能测试、事务处理、缓存、权限校验等场景。
分步理解装饰器:
(1). 需要给一个函数a()添加一个功能,功能写在decorator()内,当我们调用'decrotor(a)`的时候就会在执行a()之前执行我们要的功能。

def a():
  print('i am a')

def decorator(a):
  print('新添加的功能')
  a()

(2).我们需要该函数a()在业务逻辑代码中调用的时候名称还是a()(函数的单一职责性),而不是decrotor(a),那么最简单的方法就是在把它赋值给a,即a = decorator(a)
(3). 但是在进行a = decorator(a)时,右边的decorator(a)函数会被直接执行,而此时逻辑代码还没有调用它,为避免该情况发生,我们使用闭包的思想。
(4). 这里使用闭包思想:也就是decorator(a)返回一个函数(后面我们会把这个函数写成warpper),将这个函数赋值给a,这样在进行a = decorator(a)时右边就不会被立即执行了。最后我们把需要添加的新功能,以及a()都写在warpper函数里,这样写就可以使得只有真正在业务逻辑代码中调用该函数的时候,warpper才会被执行,同时我们会加上一句a = decorator(a),来确保业务逻辑代码中调用的时候名称还是a()。所以写出的结果如下:

def a():
  print('i am a')

def decorator(a):
  def warpper():
    print('新添加的功能')
    a()
  return warpper

a = decorator(a)
a()
# 以上写法中
decorator(a) 等价于
def warpper():
    print('新添加的功能')
    a()

最后 Python给我们给定了以个语法糖@,即可以用@decorator 代替a = decorator(a),这也就演变出装饰器最终的写法:

def decorator(a):
  def warpper():
    print('新添加的功能')
    a()
  return warpper

@decorator
def a():
  print('i am a')

a()

>>>新添加的功能
>>>i am a

装饰器采用了闭包的思想,在装饰函数的同时不执行函数,只有到正正的业务逻辑代码调用的时候再执行函数。

2.2 装饰器的执行时间

当@decorator出现的时候,decorator装饰器函数就立即被执行了。

2.3 装饰器的执行顺序

从上到下去装饰,从下到上去执行。

@a
@b
@c
def func():
# 等效于
func = a(b(c(func)))

2.4 对有参数的函数进行装饰

如果需要被装饰的的函数a()中有参数呢?这时候就需要有一个东西来接收函数a()中的参数,而函数a()中的参数也可能是多种多样的,这时候我们可以这样来写warpper函数:warpper(*args, **kwargs),但是在warpper函数中调用的的func函数也需要用:func(**args, **kwargs)的写法来解包warpper中接收的函数,这样func才可以正常执行。代码如下:

def decorator(func):
  def warpper(*args, **kwargs):
    print('新添加的功能')
    func(*args, **kwargs)
  return warpper

@decorator
def a(i='我', am='是',):
  print(i, am, "a")
  
a()
>>>新添加的功能
>>>我 是 a

2.5 对有返回值的函数进行装饰

如果需要被装饰的的函数a()中有return返回值呢?这时候就需要有一个东西来返回函数a()中的返回值,所以我们在warpper中也应该写入return的部分,代码如下:

def decorator(func):
  def warpper(*args, **kwargs):
    print('新添加的功能')
    return func(*args, **kwargs)
  return warpper

@decorator
def a(i='我', am='是',):
  print(i, am, "a")
  b = i+am+"b"
  return b

b = a()
print(b)
>>>新添加的功能
>>>我 是 a
>>>我是b

原则上要保证,装饰器中的warpper函数的格式和被装饰的函数格式一致。

2.6 带有参数的装饰器

如果我们可以给装饰器本身传入一个参数,那么装饰器就可以随着传入参数的变化而做出不同的装饰功能,例如:print('新添加的功能')、print('新添加的技术')、print('新添加的颜色')。那么,该如何实现带有参数的装饰器呢? 思路:我们定义个一个外函数,把装饰器放入其中,然后装饰器中可以使用这个外函数传入的值,同时在外函数的最后返回其中的装饰器,那么这个整体就可以成为一个新的带有参数的装饰器。代码如下:

def zhuangshiqi(i):
  def decorator(func):
    def warpper(*args, **kwargs):
      print('新添加的'+i)
      return func(*args, **kwargs)
    return warpper
  return decorator

@zhuangshiqi(i="颜色而不是功能")
def a(i='我', am='是'):
  print(i, am, "a")
  return a

a()
>>>新添加的颜色而不是功能
>>>我 是 a

实例1:简单的装饰器

def decorator(func):
  def wrapper(*args, *kwargs):
    logging.warn("%s is running" %  func.__name__)
    return func(*args, *kwargs)
  return wrapper

@decorator
def a():
  print('i am a')

a()

>>> a is running
>>> i am a

实例2:装饰器用于验证权限的例子

userAge = 40

def canYou(func):
  def decorator(*args, **kwargs):
      if userAge > 1 and userAge < 10:
          return func(*args, **kwargs)
      print('你的年龄不符合要求,不能看')
  return decorator

@canYou
def play():
  print('开始播放动画片 《喜洋洋和灰太狼》')

play()

>>> 你的年龄不符合要求,不能看

内置装饰器

@staticmathod 、@classmethod 、@property

相关文章

  • Python的自定义超时机制——装饰器的妙用

    装饰器 关于装饰器的入门,可以参考这篇文章:12步轻松搞定python装饰器简单来说,装饰器其实就是一个闭包(闭包...

  • 2020-012 python闭包与装饰器

    python闭包与装饰器 闭包 函数和对其周围状态(lexical environment,词法环境)的引用捆绑在...

  • Python装饰器-专题笔记

    学会装饰器,Python更进阶 函数作用域到闭包到装饰器讲解,及闭包和装饰器的运用。 [√] 慕课网Meshare...

  • [python] 装饰器学习

    很多python的代码都带有装饰器=。=现在不学以后也要学学一下装饰器 闭包 在学装饰器之前先看看python的闭...

  • Python 2 - 高级用法 - 装饰器

    Python 2 - 高级用法 - 装饰器 一谈到 装饰器,就离不开闭包 闭包 闭包就是能够读取其他函数内部变量的...

  • Python装饰器——初识

    上次讲了Python的闭包,今天来讲一下闭包的应用——装饰器 1. 装饰器是什么 什么叫装饰器?顾名思义,它是一个...

  • python装饰器

    装饰器简述 要理解装饰器需要知道Python高阶函数和python闭包,Python高阶函数可以接受函数作为参数,...

  • python之理解闭包和装饰器

    python之理解闭包和装饰器 1、闭包函数 1.1 python中函数都是对象 结果: 上面定义一个shut函数...

  • Python闭包与装饰器

    闭包 1.函数引用 运行结果: 2.什么是闭包 运行结果: 3.重讲闭包 内部函数对外部函数作用域里变量的引用(非...

  • 【python】闭包与装饰器

    # 本次讲述的知识点也是非常重要的(严肃脸) # 先有的闭包,之后才生成了装饰器,同样的也是非常简单的东西。 # ...

网友评论

      本文标题:Python闭包与装饰器

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