Python的装饰器和Java的注解是一回事吗?

2024-02-05 13:18
文章标签 java python 注解 装饰 回事

本文主要是介绍Python的装饰器和Java的注解是一回事吗?,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

本文为我在阅读Python’s decorators vs Java’s annotations, same thing?的笔记。

Flask 的路由看起来和 Spring 的机制非常像,比如用 Flask 定义的一个路由:将 Http 请求 GET: /hello 路由到 say_hello() 函数。

app = Flask(__name__)@app.route('/hello', methods=['GET'])
def say_hello():return 'hello'

Spring 用注解定义了路由,看起来跟 Flask 类似:


@RequestMapping(value = "/hello", method = GET)
class HelloController{@ResponseBodypublic String getHelloMessage() {return "hello";}
}

但实际上,Python 的装饰器 (decorator) 和 Java 的注解(annotation) 是完全不同的概念。Python 的装饰器本质上是返回另一个函数的函数用于装饰函数的时候,作为语法糖,实际上是函数的调用

下面我来详细解释一下这句加粗的这句话。我们定义 hello() 函数,返回一个字符串 hello:

def hello():return 'hello'if __name__ == '__main__':print(hello())

假如我们想将字符串变为 html 的标签 heading 1,可以定义一个函数提供这个转换:

def hello():return 'hello'def as_h1(message):return f'<h1>{message}</h1>'if __name__ == '__main__':print(as_h1(hello()))

如果使用装饰器实现相同的功能,只需要将 @as_h1 对 hello() 函数进行装饰:

def as_h1(func):def wrapper():result = func()return f'<h1>{result}</h1>'return wrapper@as_h1
def hello():return 'hello'if __name__ == '__main__':print(hello())

@as_h1 装饰器用于装饰 hello() 函数,本质是调用 as_h1() 函数。作为装饰器的函数要点:

  • 外函数返回内函数 : as_h1() 函数的返回值是内函数 wrapper (函数的 return wrapper 语句)
  • 内函数改变从外函数传入函数的行为:func 是传入参数,通过外函数 as_h1 传给 内函数 wrapper,在 wrapper 函数中被改变(func 函数被改变了)

之前写过一篇博文:理解和使用Python装饰器 比较详细的说明了装饰器的机制。

函数大多数是带参数的,所以一个实用的装饰器必须能处理含有参数的函数。下面的代码定义能处理带参数的装饰器,演示了装饰器的典型写法:

import time
cached_items = {}def cached(func):def wrapper(*args, **kwargs):global cached_itemif func.__name__ not in cached_items:cached_items[func.__name__] = func(*args, **kwargs)return cached_items[func.__name__]return wrapper

cached 装饰器的作用是对某个函数提供缓存机制,对某个函数比较耗时的函数,缓存可以提升效率。下面的代码演示了将 cached 装饰器作用于 intensive_task() 函数,第一次调用耗时 1 秒,第二次调用从缓存中获取,时间为 0:

@cached
def intensive_task():time.sleep(1.0)return 10start_time = time.time()
intensive_task()
print("INFO: %.8f seconds first execution" % (time.time() - start_time))start_time = time.time()
intensive_task()
print("INFO: %.8f seconds second execution" % (time.time() - start_time))

在我的 PC 上运行的结果如下:

python decorator_test2.py
INFO: 1.00221372 seconds first execution
INFO: 0.00000000 seconds second execution

而在 Java 中,注解其实就是一种特殊的注释,相当于对类、方法等贴一个标签,不会改变代码的行为。但为什么本文开头的代码中,@ReqeustMapping 注解实现了 Flask 相同的路由机制呢?其实是因为 Spring 框架发现某个方法被注解了,提供相应的功能实现而已。

比如说,定义一个 @Cached 注解,并且用在 intensiveTask() 方法上,此时与没有注解的代码,行为没有任何不同。

public class Main {@Retention(RetentionPolicy.RUNTIME)@interface Cached { } // Nothing inside our annotationstatic class SomeObject {@Cachedpublic String intensiveTask() throws InterruptedException {Thread.sleep(1000);return "expensive task result";}}public static void main(String[] args) throws Exception {long time = System.currentTimeMillis();final SomeObject expensiveTaskObject = new SomeObject();someObject.intensiveTask();System.out.println("First execution:" + (System.currentTimeMillis() - time));time = System.currentTimeMillis();someObject.intensiveTask();System.out.println("Second execution:" + (System.currentTimeMillis() - time));}
}

如果需要达到与 Python 代码相同的缓存效果,需要编写代码,将 intensiveTask() 方法加到缓存中:

static class SomeObjectExecutor {Map<String, String> cache = new HashMap<>();public String execute(SomeObject task) throws Exception {final Method intensiveTaskMethod = task.getClass().getDeclaredMethod("intensiveTask");if (intensiveTaskMethod.isAnnotationPresent(Cached.class)) {String className = task.getClass().getName();if (!cache.containsKey(className)) {cache.put(className, task.intensiveTask());}return cache.get(className);}return task.intensiveTask();}
}

然后在两次调用调用 intensiveTask() 方法的时候,通过执行 SomeObjectExecutor 类的 execute() 方法进行判断,如果缓存中已经有了 intensiveTask() 方法,则直接返回缓存中的方法,从而节省时间:

public static void main(String[] args) throws Exception {final SomeObjectExecutor someObjectExecutor = new SomeObjectExecutor();long time = System.currentTimeMillis();final SomeObject expensiveTaskObject = new SomeObject();someObjectExecutor.execute(expensiveTaskObject);System.out.println("First execution:" + (System.currentTimeMillis() - time));time = System.currentTimeMillis();someObjectExecutor.execute(expensiveTaskObject);System.out.println("Second execution:" + (System.currentTimeMillis() - time));
}

总结起来:

Java 的注解本身什么都不做,而 Python 的装饰器是函数,会改变被装饰函数的行为;
如果想改变 Java 被注解方法的行为,需要另外的代码判断某个方法是否被某个注解(名词)注解(动词),对被注解的方法提供不同的实现。

这篇关于Python的装饰器和Java的注解是一回事吗?的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/681032

相关文章

Python脚本实现自动删除C盘临时文件夹

《Python脚本实现自动删除C盘临时文件夹》在日常使用电脑的过程中,临时文件夹往往会积累大量的无用数据,占用宝贵的磁盘空间,下面我们就来看看Python如何通过脚本实现自动删除C盘临时文件夹吧... 目录一、准备工作二、python脚本编写三、脚本解析四、运行脚本五、案例演示六、注意事项七、总结在日常使用

Java实现Excel与HTML互转

《Java实现Excel与HTML互转》Excel是一种电子表格格式,而HTM则是一种用于创建网页的标记语言,虽然两者在用途上存在差异,但有时我们需要将数据从一种格式转换为另一种格式,下面我们就来看看... Excel是一种电子表格格式,广泛用于数据处理和分析,而HTM则是一种用于创建网页的标记语言。虽然两

java图像识别工具类(ImageRecognitionUtils)使用实例详解

《java图像识别工具类(ImageRecognitionUtils)使用实例详解》:本文主要介绍如何在Java中使用OpenCV进行图像识别,包括图像加载、预处理、分类、人脸检测和特征提取等步骤... 目录前言1. 图像识别的背景与作用2. 设计目标3. 项目依赖4. 设计与实现 ImageRecogni

Java中Springboot集成Kafka实现消息发送和接收功能

《Java中Springboot集成Kafka实现消息发送和接收功能》Kafka是一个高吞吐量的分布式发布-订阅消息系统,主要用于处理大规模数据流,它由生产者、消费者、主题、分区和代理等组件构成,Ka... 目录一、Kafka 简介二、Kafka 功能三、POM依赖四、配置文件五、生产者六、消费者一、Kaf

Java访问修饰符public、private、protected及默认访问权限详解

《Java访问修饰符public、private、protected及默认访问权限详解》:本文主要介绍Java访问修饰符public、private、protected及默认访问权限的相关资料,每... 目录前言1. public 访问修饰符特点:示例:适用场景:2. private 访问修饰符特点:示例:

Python将大量遥感数据的值缩放指定倍数的方法(推荐)

《Python将大量遥感数据的值缩放指定倍数的方法(推荐)》本文介绍基于Python中的gdal模块,批量读取大量多波段遥感影像文件,分别对各波段数据加以数值处理,并将所得处理后数据保存为新的遥感影像... 本文介绍基于python中的gdal模块,批量读取大量多波段遥感影像文件,分别对各波段数据加以数值处

python管理工具之conda安装部署及使用详解

《python管理工具之conda安装部署及使用详解》这篇文章详细介绍了如何安装和使用conda来管理Python环境,它涵盖了从安装部署、镜像源配置到具体的conda使用方法,包括创建、激活、安装包... 目录pytpshheraerUhon管理工具:conda部署+使用一、安装部署1、 下载2、 安装3

Python进阶之Excel基本操作介绍

《Python进阶之Excel基本操作介绍》在现实中,很多工作都需要与数据打交道,Excel作为常用的数据处理工具,一直备受人们的青睐,本文主要为大家介绍了一些Python中Excel的基本操作,希望... 目录概述写入使用 xlwt使用 XlsxWriter读取修改概述在现实中,很多工作都需要与数据打交

详解Java如何向http/https接口发出请求

《详解Java如何向http/https接口发出请求》这篇文章主要为大家详细介绍了Java如何实现向http/https接口发出请求,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 用Java发送web请求所用到的包都在java.net下,在具体使用时可以用如下代码,你可以把它封装成一

使用Python实现在Word中添加或删除超链接

《使用Python实现在Word中添加或删除超链接》在Word文档中,超链接是一种将文本或图像连接到其他文档、网页或同一文档中不同部分的功能,本文将为大家介绍一下Python如何实现在Word中添加或... 在Word文档中,超链接是一种将文本或图像连接到其他文档、网页或同一文档中不同部分的功能。通过添加超