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

目录

一、Python元编程装饰器介绍

二、装饰器使用

1. 实现认证和授权功能

2.实现缓存功能

3.实现日志输出功能

三、附录

1. logging.basicConfig介绍

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

方法一:使用datetime

方法二:使用time


一、Python元编程装饰器介绍

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

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

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

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

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

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

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

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

装饰器的使用方式如下:

def my_decorator(func):def wrapper(*args, **kwargs):# beforeprint("before...")result = func(*args, **kwargs)# afterprint("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):# beforeprint("authenticate before...")#假设在args[0]中保存了当前用户的信息if args[0].is_authenticated:# afterprint("authenticate after...")result = func(*args, **kwargs)return resultelse:# afterprint("authenticate after...")raise ValueError("用户未认证:User is not authenticated")# 返回包装后的函数return wrapperdef authorize(roles):def decorator(func):def wrapper(*args, **kwargs):# 假设在args[0]中保存了当前用户的信息if args[0].role in roles:result = func(*args, **kwargs)return resultelse:# raise ValueError("用户未授权:User is not authorized")print("用户未授权")# 返回包装后的函数return wrapperreturn decoratorclass User():def __init__(self, name, role):self.name = nameself.role = roleself.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] = resultreturn resultreturn wrapper@cache
def calculate(n):if n == 0:return 1else: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 oslogging.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 resultreturn wrapper@log
def calculate(n):if n == 0:return 1else: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 logginglogging.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 datetimenow = 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 timet = 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.rhkb.cn/news/76282.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

百度UEditor编辑器如何关闭抓取远程图片功能

百度UEditor编辑器如何关闭抓取远程图片功能 这个坑娘的功能,开始时居然不知道如何触发,以为有个按钮,点击一下触发,翻阅了文档,没有发现,然后再网络上看到原来是复制粘贴非白名单内的图片到编辑框时触发&a…

JAVA语言:如何自定义类加载器?

本文重点 前面的课程中,我们已经学习了双亲委派机制,如果想要自定义一个类加载器,那么我们只需要继承ClassLoader,并且定义好自己的findClass就可以了,也就是自己的类加载器是如何进行工作的,而loadClass就…

深度学习实战46-基于CNN的遥感卫星地图智能分类,模型训练与预测

大家好,我是微学AI,今天给大家介绍一下深度学习实战46-基于CNN的遥感卫星地图智能分类,模型训练与预测。随着遥感技术和卫星图像获取能力的快速发展,卫星图像分类任务成为了计算机视觉研究中一个重要的挑战。为了促进这一领域的研究进展,EuroSAT数据集应运而生。本文将详细…

实例031 窗体中的滚动字幕

实例说明 普通窗体中的文字位置都是固定的,一些窗体中需要让文字动起来,例如一些广告性较强的界面中需要做一些滚动的字幕。本例实现了一个具有滚动字幕效果的窗体,运行本例,单击【演示】按钮,看到窗口中的文字开始滚…

vscode 前端开发插件 2023

自己记录 安装vscode后必装插件 chinesegit 必装没啥可说 随时更新 1.CSS Navigation CTRL点击类名可跳转到对应样式位置。 如果是scss less的话。css peak插件无法生效 2.GitLens — Git supercharged 可以看到每一行的git提交记录。 3.Auto Rename Tag 可以同步更新…

第3集丨Vue 江湖 —— Vue中的事件

目录 一、Vue中事件使用二、事件修饰符2.1 prevent2.2 stop2.3 once2.4 capture2.5 self2.6 passive2.7 修饰符连续写2.8 整合案例 三、键盘事件3.1 Vue 中常用的按键别名3.2 key值绑定3.3 系统修饰符键3.4 keyCode绑定3.5 自定义别名3.6 组合键3.7 案例整合 一、Vue中事件使用…

windows docker部署

windows docker部署 使用 Docker Desktop for Windows安装Docker问题:Update the WSL kernel by running "wsl --update" or follow instructions at https://docs.microsoft.com/windows/wsl/wsl2-kernel.问题描述:解决办法,升级W…

软考A计划-系统集成项目管理工程师-信息系统安全管理-下

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例点击跳转>软考全系列点击跳转>蓝桥系列 👉关于作者 专注于Android/Unity和各种游…

前端(十一)——Vue vs. React:两大前端框架的深度对比与分析

😊博主:小猫娃来啦 😊文章核心:Vue vs. React:两大前端框架的深度对比与分析 文章目录 前言概述原理与设计思想算法生态系统与社区支持API与语法性能与优化开发体验与工程化对比总结结语 前言 在当今快速发展的前端领…

Jenkins 节点该如何管理?

Jenkins 拥有分布式构建(在 Jenkins 的配置中叫做节点),分布式构建能够让同一套代码在不同的环境(如:Windows 和 Linux 系统)中编译、测试等 Jenkins 的任务可以分布在不同的节点上运行 节点上需要配置 Java 运行时环境,JDK 版本大于 1.5 节…

917.仅仅反转字母+HJ1.字符串最后一个单词的长度

一、仅仅反转字母 917. 仅仅反转字母 - 力扣&#xff08;LeetCode&#xff09; 二、代码 双指针 class Solution { public:bool IsABC(char ch){if(ch>a &&ch<z)return true;if(ch>A && ch<Z)return true;return false;}string reverseOnlyLett…

Flutter 文件上传(七牛云)简单封装

前言&#xff1a;记录了七牛云上传图片的简单封装、若有不足 欢迎指正。 开始前准备&#xff1a; A、七牛sdk版本一定要和dart版本相对应&#xff08;推荐用any方式、让其自己去匹配&#xff09;&#xff1b; qiniu_flutter_sdk: any B、七牛上传文件所需的参数&#xff1a; …

Windows terminal 添加 git bash 解决git中文乱码显示问题

Windows terminal 添加 git bash 解决git中文乱码显示问题 在 windows terminal 中配置git 说明&#xff1a; 点击箭头选择设置 说明&#xff1a; 点击"添加新配置文件"配置名称命令行&#xff0c;可执行文件的具体语句 C:\Program Files\Git\bin\bash.exe启动目录…

Java Runnable,Callable和FutureTask详解

目 录 1 序言 2 基本概念 2.1 Runnable 和 Callable的区别 2.2 Future 和 FutureTask 2.3 ExecutorService中Future的应用 2.4 Future submit(Runnable task) 2.5 Future submit(Callable task) 2.6 ExecutorService中execute()在ThreadPoolExecutor中实现如下&#xff…

C# PaddleDetection 版面分析

效果 项目 代码 using OpenCvSharp; using OpenCvSharp.Extensions; using Sdcb.PaddleDetection; using Sdcb.PaddleInference; using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Lin…

途游游戏 x 极狐GitLab “通关” DevOps :单元测试从无到优,覆盖率 0→80%

目录 4 个工具孤岛 → 极狐GitLab 全家桶&#xff0c; 被动的「人找进度」 → 高效的「进度找人」 把 Code Review 做扎实 代码质量「向左移」&#xff0c;修复成本「往下降」 从无到「优」 自动执行单元测试&#xff0c;覆盖率 0→80% 你喜欢玩游戏吗&#xff1f; 最近…

码题集oj赛(第八次)——MT2179 01操作

一、题目 二、格式 三、样例 //输入&#xff1a; 4 10 1101 */-*-*-/*/ //输出&#xff1a; 10110注意&#xff1a; 四、代码实现 #include<bits/stdc.h> using namespace std; const int N 5e7 7; int n, m; char a[N], c; int main() {cin>>n>>m>…

MIT 6.824 -- MapReduce -- 01

MIT 6.824 -- MapReduce -- 01 引言抽象和实现可扩展性可用性(容错性)一致性MapReduceMap函数和Reduce函数疑问 课程b站视频地址: MIT 6.824 Distributed Systems Spring 2020 分布式系统 推荐伴读读物: 极客时间 – 大数据经典论文解读DDIA – 数据密集型应用大数据相关论文…

Elasticsearch官方测试数据导入

一、数据准备 百度网盘链接 链接&#xff1a;https://pan.baidu.com/s/1rPZBvH-J0367yQDg9qHiwQ?pwd7n5n 提取码&#xff1a;7n5n文档格式 {"index":{"_id":"1"}} {"account_number":1,"balance":39225,"firstnam…

Python(六十七)什么是元组

❤️ 专栏简介&#xff1a;本专栏记录了我个人从零开始学习Python编程的过程。在这个专栏中&#xff0c;我将分享我在学习Python的过程中的学习笔记、学习路线以及各个知识点。 ☀️ 专栏适用人群 &#xff1a;本专栏适用于希望学习Python编程的初学者和有一定编程基础的人。无…