什么是闭包函数与装饰器,它们有哪些作用?

图片[1]-什么是闭包函数与装饰器,它们有哪些作用?-不念博客
闭包函数与装饰器

闭包函数

  • 闭函数指的是在一个函数内部的函数,即嵌套在函数内部的函数。
  • 包函数指的是内部函数对外层函数(非全局作用域)作用域名字的引用。
  • 闭包函数基于函数对象,可以将函数返回到任意位置调用,但是作用域的关系在定义函数的时候就被确定了,与函数的调用位置无关。如果内嵌函数包含对外部函数作用域中名字的引用,该内嵌函数就是闭包函数。
# -*- coding: utf-8 -*-
def outer():
    name = 'shisbuyu'

    def inner():
        print('外层函数的内层函数inner', name)  # 内层函数使用到了外层函数名称空间中名字

    inner()
    return inner


res = outer()  # 得到的返回值是函数名inner

# 输出
# 外层函数的内层函数inner shisbuyu

闭包函数作用

  1. 函数体传参方式之 形参
def index(username):
    print(username)


index('shisbuyu')  # 函数体代码需要什么就可以在形参里写什么
  1. 函数体传参方式之 闭包
def outter():
    name = 'shisbuyu'

    def index():
        print(name)  # name写死了, 调用res() name shisbuyu

    return index


res = outer()
res()

做成闭包函数通过形参传参给外层

def outter(name):
    def index():
        print(name)

    return index


res = outter('shisbuyu')
res()  # shisbuyu
res1 = outter('shisuiyi')
res1()  # shisuiyi

装饰器

装饰器可以在不改动原函数代码的情况下,添加其原本没有的功能。简单点说,就是 修改其它函数的功能的函数 。通过使用装饰器,我们可以让一个函数的功能变的更加强大,还可以让我们的代码更加简短整洁。

普通装饰器

方式一:不使用语法糖@符号的调用方法:

# -*- coding: utf-8 -*-

# 原函数
def one():
    print("这是一个平平无奇的函数")


# 定义1个装饰器
def loop(func):
    def wrapper(*args, **kw):
        for i in range(2):
            func(*args, **kw)

    return wrapper
    
loop(one)()

# 输出
这是一个平平无奇的函数
这是一个平平无奇的函数

方式二:使用语法糖@符号的调用方法:

# -*- coding: utf-8 -*-

# 定义1个装饰器
def loop(func):
    def wrapper(*args, **kw):
        for i in range(2):
            func(*args, **kw)

    return wrapper


@loop
def one():
    print("这是一个平平无奇的函数")


one()

# 输出
这是一个平平无奇的函数
这是一个平平无奇的函数

函数带参数

# -*- coding: utf-8 -*-

def loop(func):
    def wrapper(*args, **kw):
        for i in range(2):
            func(*args, **kw)

    return wrapper


@loop
def one(num):
    for i in range(num):
        print("这是一个平平无奇的函数")


one(2)

#输出
这是一个平平无奇的函数
这是一个平平无奇的函数
这是一个平平无奇的函数
这是一个平平无奇的函数

带返回值

# -*- coding: utf-8 -*-

def loop(func):
    def wrapper(*args, **kw):
        ret = func(*args, **kw)
        return ret

    return wrapper


@loop
def one(a, b):
    return a + b


res = one(1, 2)
print(res)

# 当使用@loop对one()函数装饰以后,one指向了wrapper()函数,而wrapper()函数的返回值是ret,所以one()函数的返回值被ret接收了

#输出
3

实际案例

日志装饰器

# -*- coding: utf-8 -*-

import logging
import os

'''
* %(asctime)s   即日志记录时间,精确到毫秒@breif: 
* %(levelname)s 即此条日志级别@param[in]: 
* %(filename)s  即触发日志记录的python文件名@retval: 
* %(funcName)s  即触发日志记录的函数名
* %(lineno)s    即触发日志记录代码的行号
* %(message)s   即这项调用中的参数
'''
if not os.path.exists('Log.log'):
    file = open('Log.log', 'w')

logging.basicConfig(
    filename='Log.log',
    format="%(asctime)s - %(levelname)s - %(filename)s - %(funcName)s - %(lineno)s - %(message)s"
)

'''
* @breif: 日志修饰器,为函数添加日志记录服务
* @param[in]: err -> 发生异常时返回的错误信息
* @retval: 加载日志服务的功能函数
'''


def logger(err):
    def log(func):
        def warp(*args, **kwargs):
            try:
                result = func(*args, **kwargs)
                return result
            except Exception as e:
                logging.error(e)
                return err

        return warp

    return log


@logger('出错了')
def add(a, b):
    return a + b


print(add(1, 2))

#控制台输出
出错了
# Log.log日志显示
2022-12-13 14:11:22,772 - ERROR - 闭包函数.py - warp - 36 - unsupported operand type(s) for +: 'int' and 'str'

自动化测试脚本中处理异常

举个例子,你正在跑一个自动化测试脚本,突然间设备的闹钟响了,而刚好闹钟的界面遮挡住了你要操作的那个按钮,最终导致脚本运行失败了。没办法,你只能关掉闹钟再重新运行一次脚本。

# -*- coding: utf-8 -*-

def test_retry(func):
    def run_case_again(*args, **kwargs):
        try:
            func(*args, **kwargs)
        except:
            # 如闹铃、断网重试按钮弹出、短信通知等导致失败
            if exists(xxxx):
                touch(xxx)
            try:
                func(*args, **kwargs)
                print('用例重新执行成功')
            except:
                print('用例执行失败')
                # 其他你想做的操作,比如停止剩余用例的执行

    return run_case_again


@test_retry
def test():
    wait(xxxx, timeout=15)
© 版权声明
THE END