python的装饰器和闭包

news/2024/10/20 0:17:42

python的装饰器和闭包

内容

Python 的装饰器是什么?

装饰器(Decorator)是 Python 中的一种用于扩展或修改函数/类行为的设计模式。它允许在不修改原有函数或类的情况下,动态地为其增加新的功能。这种功能在实践中非常有用,例如在日志记录、性能测试、权限控制、缓存等场景中。


装饰器的定义与使用

  1. 装饰器的基本结构是一个函数,它接收另一个函数作为参数,并返回一个新的函数。
  2. 使用装饰器时,通常通过 @decorator_name 的语法,将其作用于某个函数或方法。

简单示例:

def my_decorator(func):def wrapper():print("调用函数前的操作")func()print("调用函数后的操作")return wrapper@my_decorator  # 等同于 say_hello = my_decorator(say_hello)
def say_hello():print("Hello, World!")say_hello()

输出:

调用函数前的操作
Hello, World!
调用函数后的操作

解释:

  1. @my_decorator 装饰器将 say_hello() 函数传递给 my_decorator()
  2. my_decorator 返回一个新的函数 wrapper,该函数包装了原始的 say_hello()
  3. 当调用 say_hello() 时,实际执行的是 wrapper(),从而在调用原函数前后添加了新的行为。

带参数的装饰器:

如果被装饰的函数接受参数,那么 wrapper 函数也需要接受相应的参数:

def my_decorator(func):def wrapper(*args, **kwargs):print("调用函数前的操作")result = func(*args, **kwargs)print("调用函数后的操作")return resultreturn wrapper@my_decorator
def add(a, b):print(f"计算 {a} + {b}")return a + bresult = add(3, 5)
print(f"结果是: {result}")

输出:

调用函数前的操作
计算 3 + 5
调用函数后的操作
结果是: 8

解释:

  • 使用 *args**kwargs,确保装饰器可以支持任意参数的函数。

多个装饰器的使用:

多个装饰器可以层层叠加,执行顺序是从上到下应用装饰器:

def decorator1(func):def wrapper():print("执行 decorator1")func()return wrapperdef decorator2(func):def wrapper():print("执行 decorator2")func()return wrapper@decorator1
@decorator2
def say_hi():print("Hi!")say_hi()

输出:

执行 decorator1
执行 decorator2
Hi!

解释:

  • @decorator1 包裹了 @decorator2,所以 decorator1 先执行。

装饰器的实际应用场景:

  1. 日志记录:
def log(func):def wrapper(*args, **kwargs):print(f"正在调用 {func.__name__} 函数")return func(*args, **kwargs)return wrapper@log
def greet(name):print(f"Hello, {name}!")greet("Alice")

输出:

正在调用 greet 函数
Hello, Alice!

  1. 权限控制:
def require_permission(permission):def decorator(func):def wrapper(*args, **kwargs):if permission != "admin":print("权限不足,无法执行该操作")returnreturn func(*args, **kwargs)return wrapperreturn decorator@require_permission("user")
def delete_user():print("用户已删除")delete_user()

输出:

权限不足,无法执行该操作

类装饰器:

除了装饰函数之外,装饰器还可以用于装饰类

def add_method(cls):cls.new_method = lambda self: print("这是新添加的方法")return cls@add_method
class MyClass:passobj = MyClass()
obj.new_method()  # 调用装饰器添加的新方法

输出:

这是新添加的方法

总结:

  • 装饰器是一种强大的工具,可以动态地为函数或类添加功能。
  • 它们通过 @decorator 的语法,使代码更加简洁和易读。
  • 常见的应用场景包括:日志记录、权限控制、缓存、性能监控等。

理解装饰器的关键在于:装饰器本质上是高阶函数,它们接收一个函数或类并返回一个新的函数或类。装饰器的使用可以大大提高代码的复用性和可维护性

什么是闭包?

闭包(Closure)是指在内嵌函数中,引用了外部函数中的变量,并且即使外部函数执行完毕,这些变量仍然保存在内嵌函数的作用域中。这种结构使得函数能够“记住”其外部环境的变量。

闭包的核心在于:

  1. 函数嵌套:函数内部定义另一个函数。
  2. 外层函数的变量被内层函数引用
  3. 外层函数的作用域结束后,变量依然保存在内层函数中

闭包的简单例子

def outer_function(x):def inner_function(y):return x + y  # 内嵌函数引用了外部函数的变量 xreturn inner_function  # 返回内嵌函数closure = outer_function(10)  # outer_function 执行完毕,返回 inner_function
result = closure(5)  # 相当于调用 inner_function(5)
print(result)  # 输出: 15

解释:

  1. outer_function 接受一个参数 x,并定义了一个内嵌函数 inner_function
  2. inner_function 使用了外部函数 outer_function 中的变量 x
  3. outer_function 执行完毕后,返回 inner_function 函数对象。
  4. 虽然 outer_function 的作用域已经结束,但由于 inner_function 是闭包,它保留了对变量 x 的引用。

闭包的作用

  • 保存状态:闭包可以记住函数的某些状态,即使外部作用域已经结束。
  • 延迟执行:闭包可以延迟计算或操作,直到真正需要时才执行。
  • 函数式编程的基础:在某些语言中(如 Python、JavaScript),闭包是实现回调、装饰器等模式的重要基础。

闭包与作用域的关系

让我们再看一个涉及作用域的闭包示例:

def counter():count = 0  # 外部变量def increment():nonlocal count  # 使用 nonlocal 声明引用外层作用域中的变量count += 1return countreturn incrementcounter_fn = counter()  # 返回内嵌的 increment 函数
print(counter_fn())  # 输出: 1
print(counter_fn())  # 输出: 2
print(counter_fn())  # 输出: 3

解释:

  • counter 函数创建了一个变量 count,并返回一个 increment 内嵌函数。
  • 每次调用 increment 时,都会更新并返回count 的值
  • 由于 increment 是闭包,它可以访问外部函数的变量 count,即使 counter 已经执行完毕。

闭包的常见应用:

1. 装饰器:

装饰器本质上就是闭包,封装了原函数及其状态。

def multiply_by(factor):def decorator(func):def wrapper(*args, **kwargs):return factor * func(*args, **kwargs)return wrapperreturn decorator@multiply_by(2)
def add(a, b):return a + bprint(add(3, 5))  # 输出: 16

解释:

  • 装饰器 multiply_by(2) 创建了一个闭包,将 factor=2 存在 wrapper 中。
  • 当调用 add(3, 5) 时,装饰器将结果乘以 2

2. 回调函数:

闭包常用于异步编程中的回调函数,保存状态直到异步操作完成。


总结:

闭包(Closure)是函数的一种特性,使得内嵌函数可以“记住”外部函数的变量,即使外部函数已经执行完毕。这种特性使得闭包在编程中非常有用,尤其是在处理状态保存、装饰器和回调函数时。

关键点:

  1. 内嵌函数引用外部函数中的变量。
  2. 外部函数执行完毕后,内嵌函数仍然可以使用这些变量。
  3. nonlocal 关键字允许内嵌函数修改外部作用域中的变量。

闭包不仅使代码更加简洁,还能实现强大的功能,是 Python 编程中的重要概念。

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

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

相关文章

《使用Gin框架构建分布式应用》阅读笔记:p88-p100

《用Gin框架构建分布式应用》学习第6天,p88-p100总结,总计13页。 一、技术总结 1.MongoDB CRUD操作 (1)InsertOne(), InsertMany() (2)Find() (3)UpdateOne, UpdateMany() (4)DeleteOne(), DeleteMany() 2.MongoDB primitive p96,recipe.ID = primitive.NewObjectID() 中的…

在blender中打开pmx文件

适用blender版本: 3.6 - 4.0 - 4.1 - 4.2 等 本人使用的blender版本为3.6 和 4.2 这里用3.6作案例下载cats插件在github中查找cats-blender-plugin 比如说这个:https://github.com/absolute-quantum/cats-blender-plugin下载最新的插件 注意: 插件版本只对应相应的blender版本…

操作系统_Paxos协议实现数据一致性更新

一、实验环境 系统:Windows10 编译软件:Visual Studio 2022 语言:C 二、内容 假设由5台服务器Ai(i=1,2..5)组成集群,每份数据在5台服务器中各保留一个副本。当客户端C1和C2同时修改存储在集群中的同一个数据时,由于网络修改延迟的存在无法保证两个数据的请求到达每台服务器…

操作系统_MPI程序设计

一、实验环境搭建 本次MPI集群环境是在电脑中安装mpi的sdk和应用程序后在visual studio 2022 上配置MPI环境。VC++目录---》包含目录---》添加MPI的include目录VC++目录---》库目录---》添加MPI的x64目录VC++目录---》预编译器---》输入“MPICH_SKIP_MPICXX”点击确认。VC++目录…

session测试

jsp1 <%@ page language="java" contentType="text/html; charset=UTF-8"pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head><meta charset="UTF-8"><title>session测试</title> </…

704.二分查找

题目 给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。示例 1:输入: nums = [-1,0,3,5,9,12], target = 9 输出: 4 解释: 9 出现在 nums 中并且下标为 4 示例 2:输入: nums = [-…

win11微软拼音输入法变繁体字

0. 设置→时间和语言 1. 时间和语言→语言和区域2. 中文简体→语言选项3. 键盘→微软拼音→键盘选项4. 常规5. 选择字符集→简体中文

泰山学堂选拔游记

泰山学堂选拔游记 前言:由于相关保密协议,所有与选拔试题与详细细节有关的内容将被剔除。 Tips:由于神秘因素,我在中学阶段的各个平台部分文章与笔记已经进行了隐藏。 插曲:等通知大学的经典通知方式 通过笔试后,要加对应取向面试群了解消息,但各个取向过笔试预留加面试…