当前位置: 首页 > news >正文

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 中可以工作。

相关文章:

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

目录 一、Python元编程装饰器介绍 二、装饰器使用 1. 实现认证和授权功能 2.实现缓存功能 3.实现日志输出功能 三、附录 1. logging.basicConfig介绍 2. 精确到毫秒,打印时间 方法一:使用datetime 方法二:使用time 一、Python元编程…...

python进程池的使用

进程池的创建 apply() apply()方法用于向进程池提交一个任务,并等待任务完成并返回结果。 apply_async() apply_async()方法用于向进程池提交一个异步任务(即无需等待任务完成),将任务加入到进程池的队列里,并立即…...

Dockerfile构建lamp镜像

1、构建目录 [rootdocker ~]# mkdir compose_lamp [rootdocker ~]# cd compose_lamp/ 2、编写Docekerfile [rootdocker compose_lamp]# vim Dockerfile #基础镜像 FROM centos:7#维护该镜像的用户信息 MAINTAINER Crushlinux <crushlinux163.com>#安装httpd RUN yum -…...

LeetCode724. 寻找数组的中心下标

题干 给你一个整数数组 nums &#xff0c;请计算数组的 中心下标 。 数组 中心下标 是数组的一个下标&#xff0c;其左侧所有元素相加的和等于右侧所有元素相加的和。 如果中心下标位于数组最左端&#xff0c;那么左侧数之和视为 0 &#xff0c;因为在下标的左侧不存在元素。…...

【云计算 | Docker】Docker容器后台运行不了?entrypoint在作妖?

1. 问题 使用镜像alpine起个容器&#xff0c;使其保持后台运行&#xff0c;正常情况有如下的效果&#xff0c;可以发现容器保持运行状态。 [rootk8s-master helloWorld]# docker run -dit docker.io/alpine /bin/sh 8d39d7579d5e4f1a560aef16ba57ab5cae2506ea9105e21cbc0634…...

DAY02_Spring第三方资源配置管理Spring容器Spring注解开发Spring整合Mybatis和Junit

目录 一 第三方资源配置管理1 管理DataSource连接池对象问题导入1.1 管理Druid连接池1.2 管理c3p0连接池 2 加载properties属性文件问题导入2.1 基本用法2.2 配置不加载系统属性2.3 加载properties文件写法 二 Spring容器1 Spring核心容器介绍问题导入1.1 创建容器1.2 获取bean…...

烘焙小程序蛋糕店烘焙店源码点心店小程序源码

本系统开发使用JAVA技术栈开发 使用uniapp技术栈 支持微信小程序 &#xff0c;对接打印机&#xff0c;对接第三方同城跑腿平台 用户端使用&#xff1a;uniapp 管理端使用&#xff1a;vueelementui 后台服务使用&#xff1a;springbootjpa...

HarmonyOS 开发基础(五)对用户名做点啥

一、实现用户名检验 条件渲染 、生命周期 1.规定用户名长度 2.限定使用的数字及字母&#xff08;涉及正则表达&#xff09; // 导出方式直接从文件夹 import MyInput from "../common/commons/myInput" Entry Component /* 组件可以基于struct实现&#xff0c;组件…...

【前端】搭建Vue3框架

目录 一、搭建准备二、node.js安装1、下载并安装2、配置默认安装目录和缓存日志目录①、创建默认安装目录和缓存日志目录&#xff08;我的node.js目录在D盘&#xff0c;所以直接在node.js文件夹下创建&#xff09;②、执行命令&#xff0c;配置默认安装目录和缓存日志目录到刚才…...

Opencv-C++笔记 (15) : 像素重映射 与 图像扭曲

文章目录 一、重映射简介二、图像扭曲 一、重映射简介 重映射&#xff0c;就是把一幅图像中某位置的像素放置到另一图像指定位置的过程。即&#xff1a; 在重映射过程中&#xff0c;图像的大小也可以同时发生改变。此时像素与像素之间的关系就不是一一对应关系&#xff0c;因…...

【Java】UWB高精度工业人员安全定位系统源码

基于VueSpring boot前后端分离架构开发的一套UWB技术高精度定位系统源码。 UWB高精度人员定位系统提供实时定位、电子围栏、轨迹回放等基础功能以及各种拓展功能,用户可根据实际需要任意选择搭配拓展功能。该系统简易部署&#xff0c;方便使用&#xff0c;实时响应。UWB高精度定…...

文本NLP噪音预处理(加拼写检查)

最近总结修改了下预处理方法&#xff0c;记录下 首先download需要的依赖 pip install pyenchantpip install nltk pyenchant 是用来检测拼写正确的&#xff0c;如果你的文本里面可能包含非正确拼写的单词&#xff0c;那就忽略它&#xff0c;nltk用来做分词的。 python -m nlt…...

[Docker实现测试部署CI/CD----自由风格的CI操作[最终架构](5)]

目录 11、自由风格的CI操作&#xff08;最终&#xff09;Jenkins容器化实现方案修改 docker.sock 权限修改 Jenkins 启动命令后重启 Jenkins构建镜像推送到Harbor修改 daemon.json 文件Jenkins 删除构建后操作Jenkins 添加 shell 命令重新构建 Jenkins通知目标服务器拉取镜像目…...

纯JS+Vue实现一个仪表盘

在使用canvas的时候发现数值变化&#xff0c;每次都要重新渲染&#xff0c;值都从0开始&#xff0c;这和我的需求冲突。 1. 先绘制基本的圆环背景&#xff0c;利用border-color和border-radius将正方形变成基本的圆环。 <div class"circle"><div class&qu…...

标定(内参、外参)

在计算机视觉中&#xff0c;特别是在相机标定和立体视觉领域&#xff0c;内参&#xff08;intrinsic parameters&#xff09;和外参&#xff08;extrinsic parameters&#xff09;是非常重要的概念。它们与相机的几何属性和姿态有关。 内参&#xff08;Intrinsic Parameters&am…...

基于ffmpeg与SDL的视频播放库

由于工作需要&#xff0c;自己封装的基于ffmpeg的视频编解码库&#xff0c;显示采用了SDL库。可以播放本地文件或网络流&#xff0c;支持多端口播放&#xff0c;支持文字叠加&#xff0c;截图、视频录制等等。 头文件代码&#xff1a; #pragma once #ifdef __DLLEXPORT #defin…...

基于二进制草蝉优化算法选择特征并使用 KNN 进行训练(Matlab代码实现)

目录 &#x1f4a5;1 概述 &#x1f4da;2 运行结果 &#x1f389;3 参考文献 &#x1f468;‍&#x1f4bb;4 Matlab代码 &#x1f4a5;1 概述 基于二进制草蝉优化算法选择特征并使用KNN&#xff08;K-Nearest Neighbors&#xff0c;K最近邻算法&#xff09;进行训练是一种…...

14-4_Qt 5.9 C++开发指南_QUdpSocket实现 UDP 通信_UDP组播

文章目录 1. UDP组播的特性2. UDP 组播实例程序的功能3. 组播功能的程序实现4. 源码4.1 可视化UI设计4.2 mainwindow.h4.3 mainwindow.cpp 1. UDP组播的特性 下图简单表示了组播的原理。UDP 组播是主机之间“一对一组”的通信模式&#xff0c;当多个客户端加入由一个组播地址定…...

ai图片合成软件帮你创造个性绚丽

嘿&#xff01;悄悄告诉你一个小秘密&#xff0c;现在有一款超酷的软件&#xff0c;它能让你的图片变得活灵活现&#xff0c;就像跳出了屏幕一样&#xff01;没错&#xff0c;这就是ai图片制作软件&#xff01;想象一下&#xff0c;你拍摄了一张美丽的风景照片&#xff0c;但总…...

git 版本回退

git 没有push之前&#xff0c;可以用git reset --mixed回退&#xff0c;就是把add 的内容和commit的内容都撤销 在push之后&#xff0c;你只有2种操作 1.git reset 退回到你想要的那个版本 有配置选项 如果是soft就是当前版本删掉&#xff0c;之前改的代码保留&#xff0c;ha…...

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…...

eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)

说明&#xff1a; 想象一下&#xff0c;你正在用eNSP搭建一个虚拟的网络世界&#xff0c;里面有虚拟的路由器、交换机、电脑&#xff08;PC&#xff09;等等。这些设备都在你的电脑里面“运行”&#xff0c;它们之间可以互相通信&#xff0c;就像一个封闭的小王国。 但是&#…...

2024年赣州旅游投资集团社会招聘笔试真

2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...

Cinnamon修改面板小工具图标

Cinnamon开始菜单-CSDN博客 设置模块都是做好的&#xff0c;比GNOME简单得多&#xff01; 在 applet.js 里增加 const Settings imports.ui.settings;this.settings new Settings.AppletSettings(this, HTYMenusonichy, instance_id); this.settings.bind(menu-icon, menu…...

跨链模式:多链互操作架构与性能扩展方案

跨链模式&#xff1a;多链互操作架构与性能扩展方案 ——构建下一代区块链互联网的技术基石 一、跨链架构的核心范式演进 1. 分层协议栈&#xff1a;模块化解耦设计 现代跨链系统采用分层协议栈实现灵活扩展&#xff08;H2Cross架构&#xff09;&#xff1a; 适配层&#xf…...

多模态大语言模型arxiv论文略读(108)

CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文标题&#xff1a;CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文作者&#xff1a;Sayna Ebrahimi, Sercan O. Arik, Tejas Nama, Tomas Pfister ➡️ 研究机构: Google Cloud AI Re…...

C#中的CLR属性、依赖属性与附加属性

CLR属性的主要特征 封装性&#xff1a; 隐藏字段的实现细节 提供对字段的受控访问 访问控制&#xff1a; 可单独设置get/set访问器的可见性 可创建只读或只写属性 计算属性&#xff1a; 可以在getter中执行计算逻辑 不需要直接对应一个字段 验证逻辑&#xff1a; 可以…...

虚拟电厂发展三大趋势:市场化、技术主导、车网互联

市场化&#xff1a;从政策驱动到多元盈利 政策全面赋能 2025年4月&#xff0c;国家发改委、能源局发布《关于加快推进虚拟电厂发展的指导意见》&#xff0c;首次明确虚拟电厂为“独立市场主体”&#xff0c;提出硬性目标&#xff1a;2027年全国调节能力≥2000万千瓦&#xff0…...

Python 高效图像帧提取与视频编码:实战指南

Python 高效图像帧提取与视频编码:实战指南 在音视频处理领域,图像帧提取与视频编码是基础但极具挑战性的任务。Python 结合强大的第三方库(如 OpenCV、FFmpeg、PyAV),可以高效处理视频流,实现快速帧提取、压缩编码等关键功能。本文将深入介绍如何优化这些流程,提高处理…...

Python网页自动化Selenium中文文档

1. 安装 1.1. 安装 Selenium Python bindings 提供了一个简单的API&#xff0c;让你使用Selenium WebDriver来编写功能/校验测试。 通过Selenium Python的API&#xff0c;你可以非常直观的使用Selenium WebDriver的所有功能。 Selenium Python bindings 使用非常简洁方便的A…...