Python元编程-装饰器介绍、使用

news2024/12/27 15:12:21

目录

一、Python元编程装饰器介绍

二、装饰器使用

1. 实现认证和授权功能

2.实现缓存功能

3.实现日志输出功能

三、附录

1. logging.basicConfig介绍

2. 精确到毫秒,打印时间

方法一:使用datetime

方法二:使用time


一、Python元编程装饰器介绍

Python元编程是指在Python中使用语言自身来创建、修改、或操纵其它代码的能力。其中,装饰器是元编程中最常见的一种技术。

装饰器是一种函数,其接受一个函数作为参数,并返回另一个函数。这通常被用于修改或扩展现有函数的功能。

装饰器在Python中的优点包括:

  • 使代码更加模块化:通过装饰器,我们可以将不同的功能逻辑分离到不同的装饰器中,使代码更加模块化。
  • 简化代码:使用装饰器可以避免代码重复,使代码更加简洁明了。
  • 提高代码的可重用性:由于装饰器本身就是一种可重用的代码模式,所以可以提高代码的可重用性。

装饰器在Python中的缺点包括:

  • 可读性较差:有时装饰器会让代码变得更加难以理解和调试。
  • 装饰器的嵌套:如果使用多个装饰器来实现某个功能,可能会导致装饰器的嵌套过于复杂。

装饰器在应用程序开发中的应用场景包括:

  • 认证和授权:通过装饰器,可以在需要进行认证和授权的函数前添加一个用于检查权限的装饰器。
  • 缓存:使用装饰器可以轻松地添加缓存逻辑,避免重复计算。
  • 日志:使用装饰器可以很方便地处理日志输出。

装饰器的使用方式如下:

def my_decorator(func):
    def wrapper(*args, **kwargs):

        # before
        print("before...")

        result = func(*args, **kwargs)

        # after
        print("after...")

        return result
    # 返回包装后的函数
    return wrapper

@my_decorator
def say_test():
    print("test")

say_test()

运行结果: 

before...
test
after...

二、装饰器使用

1. 实现认证和授权功能

下面以一个简单的例子来说明如何使用Python元编程装饰器实现认证和授权功能。

假设我们有一个网络应用程序,其中包含一些需要身份验证的受保护页面。我们希望使用装饰器来实现身份验证和授权功能。

首先,我们可以定义一个装饰器函数authenticate,用于验证用户身份:

在这个装饰器函数中,我们将原始函数func封装成一个新的函数wrapper。在wrapper函数中,我们首先检查当前用户是否已经通过身份验证,如果是,则调用原始函数func并将它的参数传递给它,否则抛出一个ValueError异常。

接下来,我们定义一个装饰器函数authorize,用于授权用户访问某个页面:

在这个装饰器函数中,我们首先接受一个roles列表,用于指定允许访问该页面的用户角色。然后,我们返回一个新的装饰器函数decorator,它接受原始函数func作为参数,并返回一个新的函数wrapper。在wrapper函数中,我们首先检查当前用户的角色是否在允许访问该页面的角色列表中,如果是,则调用原始函数func并将它的参数传递给它,否则抛出一个ValueError异常。

现在,我们可以将这两个装饰器应用到我们的页面处理函数中。例如:

def authenticate(func):
    def wrapper(*args, **kwargs):

        # before
        print("authenticate before...")
        #假设在args[0]中保存了当前用户的信息
        if args[0].is_authenticated:
            # after
            print("authenticate after...")

            result = func(*args, **kwargs)
            return result
        else:
            # after
            print("authenticate after...")

            raise ValueError("用户未认证:User is not authenticated")

    # 返回包装后的函数
    return wrapper

def authorize(roles):
    def decorator(func):
        def wrapper(*args, **kwargs):
            # 假设在args[0]中保存了当前用户的信息
            if args[0].role in roles:
                result = func(*args, **kwargs)
                return result
            else:
                # raise ValueError("用户未授权:User is not authorized")
                print("用户未授权")
        # 返回包装后的函数
        return wrapper
    return decorator

class User():
    def __init__(self, name, role):
        self.name = name
        self.role = role
        self.is_authenticated = True

@authenticate
@authorize(["admin", "editor"])
def secret_page(user):
    return "This is the secret page"

user = User("Alice", "admin")
res = secret_page(user)
print(res)

user = User("Tom", "guest")
res = secret_page(user)
# print(res)

运行结果: 

authenticate before...
authenticate after...
This is the secret page
authenticate before...
authenticate after...
用户未授权

在这个例子中,我们定义了一个User类,其中包含用户的名称、角色和身份验证状态。然后,我们将authenticateauthorize装饰器应用到secret_page函数中:authenticate用于验证用户身份,authorize用于授权用户访问该页面。在执行secret_page(user)时,Python会按照从上到下的顺序依次应用装饰器,并检查当前用户的身份和角色是否满足要求。如果满足要求,则输出页面内容,否则抛出异常。

以上就是使用Python元编程装饰器实现身份验证和授权功能的一个简单例子。当然,在实际开发中,我们需要更加细致地设计和实现这些功能,以确保应用程序的安全性和可靠性。

2.实现缓存功能

下面以一个简单的例子来说明如何使用Python元编程装饰器实现缓存功能。

假设我们有一个函数calculate,它接受一个整数作为参数,并计算该整数的阶乘。现在,我们希望使用装饰器来缓存该函数的计算结果,以提高程序的性能。

首先,我们可以定义一个装饰器函数cache,用于缓存函数调用的结果:

在这个装饰器函数中,我们首先定义一个cache_dict字典,用于保存函数调用的结果。然后,我们返回一个新的函数wrapper,它接受任意数量的参数*args,这些参数将被传递给原始函数func。在wrapper函数中,我们首先检查参数args是否已经在cache_dict中,如果是,则直接返回该结果;否则,调用原始函数func,将结果保存在cache_dict中,并返回该结果。

接下来,我们可以将cache装饰器应用到calculate函数中。例如:

def cache(func):
    cache_dict = {}
    def wrapper(*args, **kwargs):
        if args in cache_dict:
            return cache_dict[args]
        else:
            result = func(*args)
            cache_dict[args] = result
            return result
    return wrapper

@cache
def calculate(n):
    if n == 0:
        return 1
    else:
        return n * calculate(n-1)

res = calculate(5) # 第一次调用,需要计算并缓存结果
print(res)
res = calculate(5) #第二次调用,直接从缓存中获取结果
print(res)

运行结果: 

120
120

在这个例子中,我们定义了一个calculate函数,它计算给定整数的阶乘。然后,我们将cache装饰器应用到该函数中,以实现缓存功能。在执行calculate(n)时,Python会自动按照装饰器的规则调用cache(wrapper)函数,将原始的calculate函数包装成一个新的函数,并返回该函数对象。在第一次调用calculate(5)时,Python会先调用wrapper(5)函数,并将结果缓存在cache_dict中。在第二次调用calculate(5)时,Python直接从cache_dict中获取结果,并返回该结果。这样,整个计算过程只需要进行一次,大大提高了程序的性能。

以上就是使用Python元编程装饰器实现缓存功能的一个简单例子。当然,在实际开发中,我们需要更加细致地设计和实现缓存策略,以确保缓存的准确性和有效性。

3.实现日志输出功能

下面以一个简单的例子来说明如何使用Python元编程装饰器实现处理日志输出功能。

假设我们有一个函数calculate,它接受一个整数作为参数,并计算该整数的阶乘。现在,我们希望在调用该函数时自动记录日志输出,以便于调试和监控程序的运行情况。

首先,我们可以定义一个装饰器函数log,用于记录函数调用和返回结果:

在这个装饰器函数中,我们首先使用Python标准库中的logging模块来设置日志输出格式和目标文件。然后,我们返回一个新的函数wrapper,它接受任意数量的位置参数*args和关键字参数**kwargs,这些参数将被传递给原始函数func。在wrapper函数中,我们首先使用logging.debug()函数记录函数调用的信息,包括函数名、位置参数和关键字参数。然后,调用原始函数func,将结果保存在result变量中。最后,再使用logging.debug()函数记录函数返回的信息,包括函数名和返回结果。

接下来,我们可以将log装饰器应用到calculate函数中。例如:

import logging
import os

logging.basicConfig(level=logging.DEBUG,
                    format="%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s",
                    # datefmt="%a, %d, %b, %Y %H:%M:%S", # 精确到秒
                    datefmt='%Y-%m-%d %H:%M:%S', # 精确到秒
                    filename="output.log",
                    filemode="w")

def log(func):
     def wrapper(*args, **kwargs):
         logging.debug('Calling function {} with args: {}, kwargs: {}'.format(func.__name__, args, kwargs))
         result = func(*args, **kwargs)
         logging.debug('Function {} returned: {}'.format(func.__name__, result))
         return result
     return wrapper


@log
def calculate(n):
     if n == 0:
         return 1
     else:
         time.sleep(0.5)
         return n * calculate(n - 1)

# 调用函数,同时记录输出
print(calculate(5))

print(os.getcwd()) #output.log文件位置

运行结果:

120
D:\code\AutoTest\common\Metaprogramming

output.log

在这个例子中,我们定义了一个calculate函数,它计算给定整数的阶乘。然后,我们将log装饰器应用到该函数中,以实现记录日志输出功能。在执行calculate(n)时,Python会自动按照装饰器的规则调用log(wrapper)函数,将原始的calculate函数包装成一个新的函数,并返回该函数对象。在调用calculate(5)时,Python会自动调用wrapper(5)函数,并记录函数调用和返回的信息到指定的日志文件中。这样,我们就可以方便地查看函数的调用过程和计算结果,从而提高程序的可读性和可维护性。

以上就是使用Python元编程装饰器实现处理日志输出功能的一个简单例子。当然,在实际开发中,我们还需要更加细致地设计和实现日志输出策略,以满足实际的需求。

三、附录

1. logging.basicConfig介绍

logging是Python标准库中的一个模块,用于记录应用程序运行时的日志信息。其中,basicConfig函数是logging模块中的一个配置函数,用于对logging进行基本配置。

basicConfig函数可以接受多个参数,用于设置日志的格式、日志级别、输出位置等信息。常用的参数如下:

  • level:设置日志级别,可选值有DEBUG、INFO、WARNING、ERROR和CRITICAL,默认为WARNING。
  • format:设置日志格式,可选值有asctime、name、levelname、message等参数,例如:'[%(asctime)s] [%(levelname)s] %(message)s'。
  • filename:指定日志输出到文件,如果不指定则默认输出到控制台。
  • filemode:指定日志输出文件的打开模式,可选值有'w'、'a'等。

下面是一个简单的例子:

import logging

logging.basicConfig(level=logging.INFO, format='%(asctime)s %(levelname)s %(message)s')

logging.info('info message')
logging.warning('warning message')

这段代码设置了日志级别为INFO,格式为'时间 日志级别 日志信息',并输出了一个INFO级别的日志和一个WARNING级别的日志。输出结果如下:

2021-09-30 14:20:10,315 INFO info message
2021-09-30 14:20:10,315 WARNING warning message

注意,如果需要对不同的模块分别进行日志记录,则需要创建不同的Logger对象,并对其进行配置。可以使用Logger类中的方法来设置日志级别、日志格式、输出位置等信息,例如addHandler方法可以添加一个处理器来指定日志输出到文件或网络等位置。

2. 精确到毫秒,打印时间

方法一:使用datetime

你可以使用 datetime 模块来输出当前时间精确到毫秒:

from datetime import datetime

now = datetime.now()
micro_sec = now.microsecond // 1000  # 计算毫秒部分
print(now.strftime('%Y-%m-%d %H:%M:%S.') + str(micro_sec).zfill(3))

在这个代码中,我们使用 datetime.now() 函数获取当前时间,然后使用 microsecond 属性获取微秒部分,并将其除以 1000 得到毫秒。最后,我们将毫秒部分用 zfill(3) 进行前导零填充,并拼接成完整的时间字符串。

方法二:使用time

import time

t = time.time()
s = time.strftime('%Y-%m-%d %H:%M:%S.', time.localtime(t)) + ("%d" % (t % 1 * 1000)).zfill(3)
print(s)

在这个代码中,我们没有使用 %f,而是手动计算并格式化毫秒部分。具体来说,我们使用了时间戳 t 对 1 取模获得秒的小数部分,乘以 1000 得到毫秒,再使用 zfill(3) 来对毫秒部分进行前导零填充。

这种方式需要手动计算毫秒部分,并且可能会有一些精度损失,但是在 Python 2.x 中可以工作。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/820174.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Eureka 学习笔记4:EurekaClient

版本 awsVersion ‘1.11.277’ EurekaClient 接口实现了 LookupService 接口&#xff0c;拥有唯一的实现类 DiscoveryClient 类。 LookupService 接口提供以下功能&#xff1a; 获取注册表根据应用名称获取应用根据实例 id 获取实例信息 public interface LookupService<…

【MySQL】数据库基本使用

文章目录 一、数据库介绍二、数据库使用2.1 登录MySQL2.2 基本使用2.2.1 显示当前 MySQL 实例中所有的数据库列表2.2.2 创建数据库2.2.3 创建数据库表2.2.4 在表中插入数据2.2.5 在表中查询数据 三、服务器、数据库、表之间的关系四、SQL语句分类五、存储引擎 一、数据库介绍 …

多线程案例(2)

文章目录 多线程案例二二、阻塞式队列 大家好&#xff0c;我是晓星航。今天为大家带来的是 多线程案例二 相关的讲解&#xff01;&#x1f600; 多线程案例二 二、阻塞式队列 阻塞队列是什么 阻塞队列是一种特殊的队列. 也遵守 “先进先出” 的原则. 阻塞队列能是一种线程…

设计走查指南:提升设计质量的关键步骤

在产品设计过程中&#xff0c;确保产品设计质量是至关重要的。设计走查作为一种关键的质量控制方法&#xff0c;能够帮助设计团队发现问题并采取相应措施来提升设计质量。通过有效地进行设计走查&#xff0c;团队可以提高设计作品的一致性、可用性和用户满意度&#xff0c;从而…

正则表达式在格式校验中的应用以及包装类的重要性

文章目录 正则表达式&#xff1a;做格式校验包装类&#xff1a;在基本数据类型与引用数据类型间的桥梁总结 在现代IT技术岗位的面试中&#xff0c;掌握正则表达式的应用以及理解包装类的重要性是非常有益的。这篇博客将围绕这两个主题展开&#xff0c;帮助读者更好地面对面试挑…

Elasticsearch Java客户端和Spring data elasticsearch-Elasticsearch文章三

文章目录 官网版本组件版本说明实现代码地址es Spring Data Repositories例子&#xff1a;ElasticsearchRepository分析 es Spring Data Repositories 关键字es Spring Data Repositories client 加载rest风格客户端直接执行dsl例子响应式客户端-ReactiveElasticsearchClientpo…

【SLAM】LoFTR知多少

1. LoFTR: Detector-Free Local Feature Matching with Transformers PAPER 论文 | LoFTR: Detector-Free Local Feature Matching with Transformers 代码 | CODE: 关键词 | detector-free, local feature matching LoFTR知多少 1. LoFTR: Detector-Free Local Feature M…

DirectX SDK下载安装及开发环境设置

1 DirectX DirectX&#xff08;Direct eXtension&#xff0c;简称DX&#xff09;是由微软公司创建的多媒体编程接口&#xff0c;是一种应用程序接口&#xff08;API&#xff09;。DirectX可以让以windows为平台的游戏或多媒体程序获得更高的执行效率&#xff0c;加强3D图形和声…

15、两个Runner初始化器(ApplicationRunner接口和CommandLineRunner接口的实现类)

两个Runner初始化器 两个Runner初始化器——主要作用是对component组件来执行初始化 这里的Component组件我理解为是被Component注解修饰的类 Component //用这个注解修饰的类&#xff0c;意味着这个类是spring容器中的一个组件&#xff0c;springboot应用会自动加载该组件。 …

python-网络爬虫.regular

regular 正则表达式 (regular expression) 正则表达式(regular expression)描述了一种字符串匹配的模式 &#xff08;pattern&#xff09;&#xff0c; 可以用来检查一个串是否含有某种子串、将匹配的子串替换或者从某个串 中取出符合某个条件的子串等。 正则表达式是由普通…

gitlab配置webhook

一.前言 当需要做jenkins的自动化触发构建时&#xff0c;就需要配置gitlab的webhook功能&#xff0c;以下来展示以下如何配置gitlab的webhook&#xff0c;jenkins的配置就不在这里展示了&#xff0c;可以去看我devops文章的完整配置 二.配置 在新版本的gitlab中&#xff0c…

MySQL对表的操作以及数据类型

文章目录 创建删除表查看修改重命名表新增列修改列的属性删除列修改列名插入数据 数据类型enum和setenum和set的查找 创建 create table table_name ( field1 datatype, field2 datatype, field3 datatype ) charset 字符集 collate 校验规则 engine 存储引擎;其中field 表示列…

Linux - 环境变量

1.基本概念 环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数 如&#xff1a;我们在编写C/C代码的时候&#xff0c;在链接的时候&#xff0c;从来不知道我们的所链接的动态静态库在哪里&#xff0c;但 是照样可以链接成功&#xff0c;生…

超级个体新时代Web3space西南旗舰合伙人招募活动圆满落幕

7月30日&#xff0c;一场备受瞩目的超级个体新时代—Web3space西南旗舰合伙人招募活动在四川省成都市G1咖啡馆会议室成功举办。本次活动吸引了30余位Web3领域的从业者和爱好者参与&#xff0c;现场氛围十分热烈。 首先&#xff0c;CyberDAO执行合伙人JR老师主持了Web3space商业…

【AI实战】开源中文 llama2 来了,30 分钟搭建 130 亿参数大模型 Llama2-Chinese-13b-Chat

【AI实战】开源中文 llama2 来了&#xff0c;30 分钟搭建 130 亿参数大模型 Llama2-Chinese-13b-Chat 简介环境配置环境搭建依赖安装 代码及模型权重拉取拉取 Llama2-Chinese拉取 Llama2-Chinese-13b-Chat 模型权重及代码 终端测试页面测试安装 gradio加载模型并启动服务 国内 …

配置VS Code 使其支持vue项目断点调试

起因 每个应用&#xff0c;不论大小&#xff0c;都需要理解程序是如何运行失败的。当我们写的程序没有按照自己写的逻辑走的时候&#xff0c;我们就会逐步一一排查问题。在平常开发过程中我们可能会借助 console.log 来排查,但是现在我们可以借助 VS Code 断点来调试项目。 前…

Linux下查阅帮助文档必学命令 man

Linux操作系统的使用中,我们经常会遇到很多问题,这个时候查询文档的能力至关重要,黄老师来推荐大家使用man,这时我们必须掌握的查阅能力: 最常用的命令: man 名称 man 数字(1~9) 名称 这里的数字分别代表:

JavaWeb 项目实现(四) 验证旧密码

1.验证旧密码 步骤很简单&#xff0c;从Session中取到当前密码&#xff0c;和修改密码界面得到的旧密码对比&#xff0c;判断是否相等。 特别之处在于实现用到了Ajax&#xff0c;可以不刷新整个页面的情况下与Web服务器进行通信。 2.Ajax Ajax&#xff08;Asynchronous Java…

使用Gunicorn+Nginx部署Flask项目

部署-开发机上的准备工作 确认项目没有bug。用pip freeze > requirements.txt将当前环境的包导出到requirements.txt文件中&#xff0c;方便部署的时候安装。将项目上传到服务器上的/srv目录下。这里以git为例。使用git比其他上传方式&#xff08;比如使用pycharm&#xff…

深度学习之用PyTorch实现线性回归

代码 # 调用库 import torch# 数据准备 x_data torch.Tensor([[1.0], [2.0], [3.0]]) # 训练集输入值 y_data torch.Tensor([[2.0], [4.0], [6.0]]) # 训练集输出值# 定义线性回归模型 class LinearModel(torch.nn.Module):def __init__(self):super(LinearModel, self)._…