闭包和装饰器(四)

一、闭包

问题一:函数内嵌套一个函数,在函数外是否可以调用?

答:不可调用

def func():
    print("----func----")
    def wrapper():
        print("----wrapper----"return wrapper
注意:外层函数返回的就是内层函数的函数名,若return wrapper(),则已经直接调用了内层函数,那么外层返回的是内层函数的返回结果:None

问题二:什么是闭包?

满足闭包的三个条件:

1.函数中嵌套一个函数

2.外层函数的返回值是内层函数的函数名

3.内层嵌套函数对外部作用域有一个非全局变量的引用

  全局变量:模块定义的变量

  局部变量:函数内部定义的变量 or 传进函数的参数

闭包的作用:

1.实现数据锁定

def fun2():
  num = 100
  def wrapper():
    print("这里使用了num的值:{}".format(num))
  return wrapper

wrapper = fun2()
num = 1000
wrapper()

运行结果:发现全局变量修改内部变量无效。

  

二、装饰器(decorator)

开放封闭原则:软件实体应该是可扩展而不可修改。也就是说对扩展是开放的,而对修改是封闭的。

  封闭:已实现的功能代码,对内部修改是封闭的。

  开放:对功能的扩展是开放的。

装饰器的作用:可以在不修改原功能代码的同时给它扩展新的功能,同时不改变调用方式。

@语法糖

普通装饰器

#装饰器函数(通过闭包实现)
def decorator(func):
 print("---decorator被调用了---")
def wrapper(): print("这个是装饰器函数") func() return wrapper @decorator #作用等于在函数定义之后执行 func = decorator(func) def func(): print("这个是原功能函数func") # func = decorator(func) # @decorator() func()

装饰器执行原理:先执行装饰器函数(@decorator)返回wrapper,再执行wrapper(),在wrapper中,func函数作为装饰器参数func传入,执行原func函数

装饰器常见运用场景

1.权限校验

2.用来给函数运行计算时间

3.环境准备和恢复的工作

4.web自动化用例失败截图

from selenium import webdriver
brower = webdriver.Chrome()
brower.get("https://www.baidu.com")

def decorator(func):
  def wrapper():
    try:
      func()
    except:
      brower.save_screenshot('error.png')
      raise
  return wrapper

@decorator
def test_search():
  brower.find_element_by_xpath("//input[@id = 'kw]")
  brower.find_element_by_xpath("//input[@id = 'su11']").click()

test_search()

带参数的装饰器

def decorator(func):
  def wrapper(*args, **kwargs):
    print('这是个装饰器函数')
    func(*args, **kwargs)
  return wrapper

@decorator
def add_num(a, b):
  print('计算a+b的结果', a + b)
  
add_num(a=11, b=22)

@decorator
def func(*args):
  print(*args)
  
tu =(11,22,33)
func(*tu)

类实现装饰器 

__call__:魔术方法:在对象使用()调用的时候被触发

class MyDecorator(object):
  def __init__(self, func):
    self.func = func
  def __call__(self, *args, **kwargs):
    print('这个是__call__方法')
    self.func(*args, **kwargs)
    

@MyDecorator
def add_num():
  print('计算a+b的结果')
  
add_num()

 类装饰的工作流程可以分为两部分:装饰阶段和调用阶段。解释器一遇到“@MyDecorator”语句时就会完成对add_num( )函数的装饰,即调用MyDecorator类的__init__( )方法;当代码执行到add_num( )时,会执行装饰类MyDecorator的__call__( )方法,实现对add_num( )函数的调用。

虽然"@MyDecorator"后面没有括号,也没有参数,但实际上它是有参数的,被装饰的对象add_num()就是@MyDecorator的默认参数,函数对象被传入到装饰器类的构造方法__init__()中,并在__call__()方法中调用。当然我们也可以通过"@MyDecorator(args)"的语法像类装饰器传递其他参数,但是需要值得注意的是:一旦在@MyDecorator()中的参数被传入到__init__()方法,被装饰函数对象add_num()无法同时传入。解决办法:将被装饰函数对象传入__call__()方法。

如下图代码:在MyDecorator类的__call__( )方法中定义了一个内部函数(闭包)接收add_num( )函数中的参数,并实现对被装饰函数的调用。

class MyDecorator(object):
  def __init__(self, desc):
    print('__init__')
    print(desc)
  def __call__(self, func):
    print('这个是__call__方法')
    def wrapper(*args, **kwargs):
       return func(*args, **kwargs)
    return wrapper  
    

@MyDecorator('百家号'def add_num():
  print('计算a+b的结果')
  
add_num()

执行结果:

参考:https://baijiahao.baidu.com/s?id=1627531125454422806&wfr=spider&for=pc

posted @ 2019-08-13 14:18  sinder2018  阅读(38)  评论(0)    收藏  举报