美文网首页python
python笔记11:装饰器

python笔记11:装饰器

作者: _百草_ | 来源:发表于2022-04-19 10:18 被阅读0次

    1. 什么是装饰器

    函数也是一个对象,并且可以赋值给变量==>通过变量调用该函数

    def main():
        demo_func()
    
    
    if __name__ == "__main__":
        a = main  # 函数对象赋值给变量;这里没有使用小括号,因为我们并不是在调用main函数
        a()  # 通过变量调用该函数
        print(a.__name__)  # 获取函数名 输出:main
        print(main.__name__)  # 获取函数名 输出:main
        
        # 删除对象
        del main
        a()  # 再次调用,正常
        main()  # 再次调用报错;NameError: name 'main' is not defined
    
    

    期望:增强main()函数功能,如执行前后打印log,但不修改main()函数定义
    ==>在代码运行期间增加功能的方式,称之为“装饰器”
    本质上,装饰器decorator是一个返回函数的高阶函数
    即:改变其他函数功能的函数
    通过装饰器函数,来增强原函数的一些功能,且原函数不需要修改

    1.1 在函数中定义函数

    def main():
        print("这是main函数!")
        def hello():
            print("这是Hello函数!")
        def world():
            print("这是World函数!")
        return hello(), world()  # 调用函数内函数
    
    main()  # 调用main(),函数内函数hello()和world()将同时调用
    hello()  # 函数main()外,无法直接访问函数内函数hello(),报错NameError: name 'hello' is not defined
    

    1.2 从函数中返回函数

    def main(name=""):
        def welcome():
            return "welcome"
    
        def other():
            return name
    
        if name:
            return other  # 不需要在一个函数中执行另一个函数
        else:
            return welcome
    
    
    a = main("hello")  # 若不加小括号则是赋值,不是调用
    print(a)  # 输出<function main.<locals>.other at 0x000001271DDAE1F0>
    print(a())  # a 指向函数,返回加小括号调用该函数
    

    1.3 将函数作为参数传给另一函数

    def main():
        return "这是main函数!"
    
    def before(func):  # 一般func代指函数
        print("执行前调用")
        print(func())  # 函数调用
    
    before(main)  # 注意传参是函数,此处不用小括号
    

    1.4 第一个装饰器

    # 1---------------------
    # # 没有返回函数,报错:TypeError: 'NoneType' object is not callable
     def print_log(func):
         print(f"开始执行{func.__name__}!")
         func()
         print(f"结束执行{func.__name__}!")
    # 1---------------------
    
    
    # 2--------------------
    # 问题:函数func重复执行
     def print_log(func):
        print(f"开始执行{func.__name__}!")
         func()
         print(f"结束执行{func.__name__}!")
         return func
    # 2--------------------
    
    # 3 使用函数内函数添加功能,函数返回该内部函数即可
    def print_log(func): # 接收一个函数作为参数
        def wrapper():
            print(f"开始执行{func.__name__}!")
            func()
            print(f"结束执行{func.__name__}!")
        return wrapper  # 返回一个函数
    
    
    # 方式1:使用装饰器的函数上方添加@func_name
    @print_log
    def main():
        print("正在执行main...")
    
    
    main()
    
    # # 方式2:直接调用
    # a = print_log(main)
    # a()
    

    装饰器调用:
    @称之为语法糖,相当于a = print_log(main)

    @log  # 使用@decorator_name ,放置在函数定义前;
    # 注意:使用的装饰器(函数),后面不需要添加小括号
    def main():
        print("main")
    

    在面向对象OOP的设计模式中,decorator被称为装饰模式。OOP的装饰模式需要通过继承和组合来实现。

    1. 支持OOP的decorator
    2. 从语法层次支持decorator。Python的decorator可以用函数实现,也可以用类实现。
    • 缺点:定义起来虽然有点复杂
    • 优点:使用起来非常灵活和方便;加强函数功能

    2. 简单的装饰器

    装饰器常用场景:日志、授权
    通常情况下,会把*args**kwargs,作为装饰器内部函数wrapper()的参数=>表示接受任意数量和类型的参数。

    import functools
    
    
    # 装饰器
    def decorator_name(func):
        @functools.wraps(func)
        def decorated(*args, **kwargs):
            if not can_run:
                return "function will not run"
            return func(*args, **kwargs)
        return decorated
    
    
    @decorator_name
    def funct(name):
        return f"Function is running!-name={name}"
    
    
    can_run = True
    print(funct("wlh"))   # 输出:Function is running!-name=wlh
    can_run = False
    print(funct("百草"))  # 输出:function will not run
    

    3. 带参数的装饰器

    • 3层嵌套,定义带参数的装饰器
    • 装饰器使用时,使用单一函数作为参数的
    • 需要带带参数的装饰器时,需要再添加一个包裹函数,返回简单的装饰器即可
    from functools import wraps
    
    
    def log_var(filename):  # 创建一个包裹函数
        def print_log(func):  # 简单装饰器
            @wraps(func)
            def wrapper(*args, **kwargs):  # 为另一函数附加功能的函数
                print("开始执行")
                with open(filename, "a", encoding="utf-8") as f:
                    f.write("开始执行!")
                func(*args, **kwargs)
                print("结束执行")
                with open(filename, "a", encoding="utf-8") as f:
                    f.write("结束执行!")
    
            return wrapper
        return print_log  # 返回简单装饰器
    

    4. 类装饰器

    类也可以用来构建装饰器
    类装饰器,主要依赖于函数__call__,每当调用一个实例时,函数__call__就会被执行一次。

    from functools import wraps
    
    
    class Log:
        """
        比方说有时你只想打日志到一个文件。
        而有时你想把引起你注意的问题发送到一个email,同时也保留日志,留个记录。
        这是一个使用继承的场景,但目前为止我们只看到过用来构建装饰器的函数。
        """
        def __init__(self, filename="out.log"):
            self.filename = filename
    
        def __call__(self, func):
            @wraps(func)
            def wrapper(*args, **kwargs):
                print("开始执行!")
                print(self.filename)
                with open(self.filename, 'a', encoding="utf-8") as f:
                    # 打印日志
                    f.write("开始执行!\n")
                self.notify()  # 发送通知
                return func(*args, **kwargs)
            return wrapper
    
        def notify(self):
            """发送通知"""
            print("发送通知")
            pass
    
    
    # 继承,创建子类
    class EmailLog(Log):
        """一个logit的实现版本,可以在函数调用时发送email给管理员"""
    
        def __init__(self, email, *args, **kwargs):
            self.email = email
            super(EmailLog, self).__init__(*args, **kwargs)  # 继承类
    
        def notify(self):
            # 发送邮件到指定邮箱
            print("发送邮件到指定邮箱")
    
    
    # @Log()  # 调用修饰器,注意类装饰器使用时,需要小括号
    # def main():
    #     print("执行main")
    #
    #
    # main()
    
    
    @EmailLog("123")
    def two():
        print("执行two")
    
    two()
    

    5. 装饰器嵌套


    6. @functools.wrap装饰器使用

    print(main.__name__)  # 输出结果:wrapper;即重写了函数的名字和注释文档docstring
    print(main.__doc__)  # 输出结果:这是wrapper的docstring
    
    """
    使用functools.wraps来解决函数名和注释文档的重写
    """
    from functools import wraps
    
    
    def new_decorator(func):
        """这是new_decorator的docstring"""
        @wraps(func)  # 在定义的wrapper上添加休修饰器即可
        def wrapper():
            """这是wrapper的docstring"""
            print("执行前!")
            func()
            print("执行后!")
        return wrapper
    
    
    @new_decorator
    def requiring_decorator():
        """这是requiring_decorator的docstring"""
        print("这是requiring decorator函数!")
    
    
    print(requiring_decorator.__name__)
    print(requiring_decorator.__doc__)
    
    

    注:

    1. python3-装饰器基础使用
    2. Learn python 3:装饰器
    3. Python 函数装饰器
    4. 装饰器-廖雪峰的官方网站

    相关文章

      网友评论

        本文标题:python笔记11:装饰器

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