美文网首页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:装饰器

    1. 什么是装饰器 函数也是一个对象,并且可以赋值给变量==>通过变量调用该函数 期望:增强main()函数功能,...

  • Python ☞ day 5

    Python学习笔记之 装饰器& 偏函数 & 异常处理 & 断言 & 文件读写 &编码与解码 装饰器 概念:是一个...

  • 大师兄的Python学习笔记(十四): 迭代器、生成器和协程

    大师兄的Python学习笔记(十三): 理解装饰器大师兄的Python学习笔记(十五): Socket编程 一、关...

  • 装饰器模式

    介绍 在python装饰器学习 这篇文章中,介绍了python 中的装饰器,python内置了对装饰器的支持。面向...

  • Python—闭包与装饰器

    将之前学习Python的笔记整理记录下来。 闭包 装饰器

  • python中的装饰器

    python装饰器详解 Python装饰器学习(九步入门) 装饰器(decorator) 就是一个包装机(wrap...

  • [译] Python装饰器Part II:装饰器参数

    这是Python装饰器讲解的第二部分,上一篇:Python装饰器Part I:装饰器简介 回顾:不带参数的装饰器 ...

  • Python 装饰器笔记

    装饰器的主要作用:代码复用、装饰函数! 简单装饰器 现在有三个函数 每个函数都有自己的功能! 这时我想让这些函数在...

  • Python装饰器笔记

    一.函数装饰器 1.从Python内层函数说起 首先我们来探讨一下这篇文章所讲的内容Inner Functions...

  • python笔记:装饰器

    闭包 在学习装饰器前,我先遇上了这样的写法 上面这一小段代码,fun函数有以下特点 fun函数内嵌套定义了个函数i...

网友评论

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

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