基本概念 定义 装饰器本质上是一个函数,该函数接受一个函数作为参数,并返回一个新的函数。(可以理解为输入一个函数,返回装饰后的函数,所谓装饰,一般来说就是为该函数添加一些功能)
装饰器的核心部分只有两部分:
定义一个内部函数:为原函数添加功能(在参数与返回类型上应与原函数保持一致)
返回该内部函数
这是一个简单装饰器的例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import timedef calculate (a,b ): sum =0 for i in range (a,b): sum = sum +i print (sum ) return sum def decorator_cal (func ): def wapper (*args, **kwargs ): start_time = time.time() res = func(*args, **kwargs) end_time =time.time() diff_time = end_time-start_time print (f"{func.__name__} took {diff_time:.10 f} seconds" ) return res return wapper calculate= decorator_cal(calculate) calculate(6 ,10263 )
首先我们定义了一个简单的求和函数calculate(a,b),输入是区间[a,b),返回区间和。
接下来我们定义了一个装饰器函数
其中的关键是
*args, **kwargs:这些是可变参数,用于接收任意数量的非关键字参数和关键字参数。这样可以确保 wapper
函数能够接收和处理 func
所有的参数。
res = func(*args, **kwargs):调用传入的 func
函数,并把原始的参数传递给它。执行完 func
函数后,会将结果保存在变量 res
中。
return res 由于原函数包含返回值,所以wapper也需要有和原函数相同的返回值。如果原函数没有返回值,wapper自然也不需要返回值。
最后我们显示调用该装饰器函数:calculate= decorator_cal(calculate),从此以后,调用calculate就是调用装饰过后的calculate了(注意我们一般通过@符号完成该步骤,这里是方便理解)
@符号 @decorator本质上是一个语法糖,当他添加在某函数(func)上时,它等价与func = decorator(func)
装饰器通常使用@decorator_name
的语法糖直接应用到需要装饰的函数上。
带参数的装饰器 有时,装饰器本身需要接受参数。此时可以使用多一层嵌套来实现。
比如,我想控制打印精度
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import timedef Precision_control (precision ): def decorator_cal (func ): def wapper (*args, **kwargs ): start_time = time.time() res = func(*args, **kwargs) end_time =time.time() diff_time = end_time-start_time print (f"{func.__name__} took {diff_time:.{precision} f} seconds" ) return res return wapper return decorator_cal@Precision_control(5 ) def calculate (a,b ): sum =0 for i in range (a,b): sum = sum +i print (sum ) return sum calculate(6 ,10263 )
示例 我们可以通过 Python 装饰器来实现多种功能,比如日志记录、权限验证、性能测量、缓存、输入检查等。
日志记录(Logging) 日志记录用于在函数执行时记录相关信息,帮助调试和了解系统行为。可以通过装饰器自动记录函数的调用情况。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import loggingfrom functools import wraps logging.basicConfig(level=logging.INFO)def log_decorator (func ): @wraps(func ) def wrapper (*args, **kwargs ): logging.info(f"Running {func.__name__} with args: {args} and kwargs: {kwargs} " ) result = func(*args, **kwargs) logging.info(f"{func.__name__} returned {result} " ) return result return wrapper@log_decorator def add (a, b ): return a + b add(2 , 3 )
日志输出:
1 2 INFO:root:Running add with args: (2, 3) and kwargs: {} INFO:root:add returned 5
权限验证(Authentication) 权限验证用于在执行某些敏感操作之前验证用户身份或权限。可以使用装饰器来检查用户是否有权限执行特定操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 def check_permissions (user ): def decorator (func ): @wraps(func ) def wrapper (*args, **kwargs ): if user['role' ] != 'admin' : raise PermissionError(f"User {user['name' ]} does not have admin rights" ) return func(*args, **kwargs) return wrapper return decorator user = {'name' : 'Alice' , 'role' : 'user' }@check_permissions(user ) def delete_data (): print ("Data deleted." )try : delete_data()except PermissionError as e: print (e)
输出:
1 User Alice does not have admin rights
性能测量用于评估函数的执行时间。可以通过装饰器来记录函数的开始时间和结束时间,并计算执行时间。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import timedef timer_decorator (func ): @wraps(func ) def wrapper (*args, **kwargs ): start_time = time.time() result = func(*args, **kwargs) end_time = time.time() print (f"{func.__name__} took {end_time - start_time:.4 f} seconds" ) return result return wrapper@timer_decorator def slow_function (): time.sleep(2 ) return "Finished" slow_function()
输出:
1 slow_function took 2.0001 seconds
缓存(Caching) 缓存用于存储函数的返回值,从而避免多次计算相同输入的结果。可以使用装饰器来实现简单的缓存机制。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 def cache_decorator (func ): cache = {} @wraps(func ) def wrapper (*args ): if args in cache: print ("Using cached result" ) return cache[args] result = func(*args) cache[args] = result return result return wrapper@cache_decorator def expensive_computation (x ): print (f"Computing for {x} " ) return x * x expensive_computation(4 ) expensive_computation(4 )
输出:
1 2 Computing for 4 Using cached result
输入检查(Validation) 输入检查用于验证函数的参数是否合法,避免无效的输入导致错误。可以使用装饰器自动检查参数的类型或范围。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 def validate_decorator (func ): @wraps(func ) def wrapper (a, b ): if not isinstance (a, int ) or not isinstance (b, int ): raise ValueError("Both arguments must be integers" ) return func(a, b) return wrapper@validate_decorator def add (a, b ): return a + btry : add(2 , "three" )except ValueError as e: print (e)
输出:
1 Both arguments must be integers
重试机制(Retry) 重试机制用于在函数失败时自动重试,特别是在处理不稳定的外部资源(如网络请求)时很有用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 def retry_decorator (retries=3 ): def decorator (func ): @wraps(func ) def wrapper (*args, **kwargs ): attempt = 0 while attempt < retries: try : return func(*args, **kwargs) except Exception as e: print (f"Attempt {attempt + 1 } failed with error: {e} " ) attempt += 1 raise Exception(f"Function failed after {retries} retries" ) return wrapper return decorator@retry_decorator(retries=5 ) def unstable_function (): raise ValueError("Random failure" )try : unstable_function()except Exception as e: print (e)
输出:
1 2 3 4 5 6 Attempt 1 failed with error: Random failure Attempt 2 failed with error: Random failure Attempt 3 failed with error: Random failure Attempt 4 failed with error: Random failure Attempt 5 failed with error: Random failure Function failed after 5 retries
@atexit装饰器 atexit
装饰器用于在程序退出之前自动执行某些操作,比如清理资源或记录日志。Python的atexit
模块可以注册函数在程序退出时执行。
1 2 3 4 5 6 import atexitdef goodbye (): print ("Program is exiting. Goodbye!" ) atexit.register(goodbye)
当程序正常退出时,会打印:
1 Program is exiting. Goodbye!
@deprecated装饰器 deprecated
装饰器用于标记一个函数已不推荐使用,提醒开发者在调用时使用替代方案。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import warningsdef deprecated (func ): @wraps(func ) def wrapper (*args, **kwargs ): warnings.warn(f"{func.__name__} is deprecated" , category=DeprecationWarning) return func(*args, **kwargs) return wrapper@deprecated def old_function (): print ("This function is old and deprecated." ) old_function()
输出:
1 2 old_function is deprecated This function is old and deprecated.
Django中的装饰器 在 Django 框架中,装饰器广泛用于简化代码、增强功能和实现一些常见的功能需求。
功能:
@login_required
是 Django 中最常用的装饰器之一,确保用户在访问特定视图之前必须已经登录。它会检查当前请求的用户是否已经登录,未登录的用户将被重定向到登录页面。
使用示例:
1 2 3 4 5 from django.contrib.auth.decorators import login_required@login_required def my_view (request ): return HttpResponse("You are logged in!" )
如果用户没有登录,Django 会将用户重定向到默认的登录页面(可以通过 LOGIN_URL
配置自定义登录页面)。
自定义登录页面:
1 2 3 @login_required(login_url='/custom-login/' ) def my_view (request ): return HttpResponse("Custom login page if not authenticated." )
@permission_required 功能:
@permission_required
用于检查用户是否具有特定的权限。它要求用户具备指定的权限才能访问视图。如果用户不具备该权限,系统会显示一个“403 Forbidden”错误或将用户重定向到指定页面。
使用示例:
1 2 3 4 5 from django.contrib.auth.decorators import permission_required@permission_required('app_label.permission_codename' ) def my_view (request ): return HttpResponse("You have the necessary permission!" )
可以自定义权限不足时的重定向页面:
1 2 3 @permission_required('app_label.permission_codename' , login_url='/no-permission/' ) def my_view (request ): return HttpResponse("You are redirected due to insufficient permissions." )
@user_passes_test 功能:
@user_passes_test
是一个通用装饰器,允许你根据自定义的测试条件决定是否允许用户访问某个视图。你可以通过传递一个函数来进行验证。如果测试未通过,用户将被重定向到登录页面或指定页面。
使用示例:
1 2 3 4 5 6 7 8 from django.contrib.auth.decorators import user_passes_testdef check_if_superuser (user ): return user.is_superuser@user_passes_test(check_if_superuser ) def superuser_view (request ): return HttpResponse("You are a superuser." )
自定义重定向页面:
1 2 3 @user_passes_test(check_if_superuser, login_url='/no-access/' ) def superuser_view (request ): return HttpResponse("Only superusers can see this." )
@csrf_exempt 功能:
Django 默认开启了跨站请求伪造 (CSRF) 防护机制。@csrf_exempt
装饰器用于排除某些视图不进行 CSRF 检查,特别是在处理第三方系统的请求时非常有用。
使用示例:
1 2 3 4 5 from django.views.decorators.csrf import csrf_exempt@csrf_exempt def my_view (request ): return HttpResponse("CSRF protection is disabled for this view." )
注意:在禁用 CSRF 防护时要小心,确保这个视图只用于安全的外部系统或只接受安全的请求。
@require_http_methods 功能:
@require_http_methods
限制视图只允许通过指定的 HTTP 方法(如 GET、POST 等)访问。它是简化对请求类型验证的一种快捷方式。
使用示例:
1 2 3 4 5 from django.views.decorators.http import require_http_methods@require_http_methods(["GET" , "POST" ] ) def my_view (request ): return HttpResponse("This view only allows GET and POST requests." )
如果客户端发送了非指定的 HTTP 方法(如 PUT、DELETE 等),服务器将返回 405 状态码(Method Not Allowed)。
@require_GET, @require_POST 功能:
@require_GET
:确保视图只能通过 GET 请求访问。
@require_POST
:确保视图只能通过 POST 请求访问。
使用示例:
1 2 3 4 5 6 7 8 9 from django.views.decorators.http import require_GET, require_POST@require_GET def my_get_view (request ): return HttpResponse("This view only allows GET requests." )@require_POST def my_post_view (request ): return HttpResponse("This view only allows POST requests." )
@cache_page 功能:
@cache_page
装饰器用于缓存视图的输出。它可以显著提升性能,尤其是对于经常被访问但不常变化的页面。
使用示例:
1 2 3 4 5 from django.views.decorators.cache import cache_page@cache_page(60 * 15 ) def my_view (request ): return HttpResponse("This view is cached for 15 minutes." )
你可以通过缓存的时间长度来控制缓存失效的时间(以秒为单位)。此外,你可以根据 URL 或请求参数实现更复杂的缓存策略。
@never_cache 功能:
@never_cache
装饰器确保视图的响应永远不会被缓存。它可以用于那些内容经常变化且不应该被缓存的视图。
使用示例:
1 2 3 4 5 from django.views.decorators.cache import never_cache@never_cache def my_view (request ): return HttpResponse("This view should never be cached." )
功能:
@vary_on_headers
:根据请求的 HTTP 头信息来区分缓存内容。
@vary_on_cookie
:根据请求中的 Cookie 信息来区分缓存内容。
使用示例:
1 2 3 4 5 6 7 8 9 from django.views.decorators.vary import vary_on_headers, vary_on_cookie@vary_on_headers("User-Agent" ) def my_view (request ): return HttpResponse("Vary on User-Agent." )@vary_on_cookie def my_cookie_view (request ): return HttpResponse("Vary on cookies." )
这些装饰器通常结合缓存机制一起使用,可以实现针对不同用户、浏览器或请求的缓存控制。
@transaction.atomic 功能:
@transaction.atomic
装饰器用于将视图的数据库操作封装在一个事务中。如果在视图执行过程中出现错误,所有的数据库操作都将回滚,确保数据一致性。
使用示例:
1 2 3 4 5 6 from django.db import transaction@transaction.atomic def my_view (request ): ...
如果在视图中抛出异常,数据库的变更将不会被保存。
@sensitive_post_parameters 功能:
@sensitive_post_parameters
用于在处理敏感数据(如密码、信用卡号等)的视图中,防止这些数据出现在错误报告或日志中。它会将指定的 POST 参数标记为敏感信息,避免暴露。
使用示例:
1 2 3 4 5 6 7 from django.views.decorators.debug import sensitive_post_parameters@sensitive_post_parameters('password' , 'credit_card_number' ) def my_view (request ): password = request.POST['password' ] credit_card_number = request.POST['credit_card_number' ] ...
Python装饰器与Java注解 Python 装饰器和 Java 注解是两种不同语言中用于增强代码功能的机制,虽然它们的语法和使用方式不同,但都可以用来对函数、类、方法或变量进行修饰或附加行为。
定义与用途
Python 装饰器 : Python 装饰器是一种高阶函数,通常用于在不改变原有函数或方法代码的情况下,为其添加额外的功能。装饰器接受一个函数或类作为参数,返回一个被包装后的函数或类。常见用途包括日志记录、权限检查、性能监测、缓存等。
Java 注解 : Java 注解(Annotation)是一种元数据标注形式,它为类、方法、字段等元素提供额外的信息。注解本身并不直接执行代码,而是通过编译器、框架或运行时机制(如反射)解析注解,并做出相应的处理。常见用途包括文档生成、代码检查、运行时行为定制等。
运行机制 Python 装饰器
Java 注解
常见用途 Python 装饰器的用途
日志记录 :在函数执行前后记录日志信息。
权限检查 :在访问敏感功能前进行权限验证。
性能监测 :测量函数执行的时间。
缓存 :缓存函数结果,避免重复计算。
输入校验 :对函数输入进行参数校验。
示例:权限检查装饰器
1 2 3 4 5 6 7 8 9 10 def require_admin (func ): def wrapper (user, *args, **kwargs ): if not user.is_admin: raise PermissionError("Admin privileges required" ) return func(user, *args, **kwargs) return wrapper@require_admin def delete_user (user ): print (f"User {user.name} deleted." )
Java 注解的用途
编译时检查 :@Override
用于检查方法是否正确覆盖了父类的方法。
依赖注入 :@Autowired
用于 Spring 框架中进行依赖注入。
事务管理 :@Transactional
用于声明方法或类是事务性操作。
映射关系 :@Entity
、@Table
等用于 Hibernate 框架中映射数据库表。
测试 :@Test
用于 JUnit 测试框架标识测试方法。
示例:Spring 中的事务管理
1 2 3 4 5 6 7 8 9 import org.springframework.transaction.annotation.Transactional;public class MyService { @Transactional public void performOperation () { } }
差异与对比
维度
Python 装饰器
Java 注解
处理时间
运行时动态处理
编译时和运行时(通过反射或框架处理)
功能
直接修改或增强函数/类的行为
提供元数据,不直接修改行为,通常通过框架或工具解析
灵活性
非常灵活,可用于任意函数或类,动态修改代码
注解多为框架或工具依赖,元数据驱动
常见用途
日志、权限验证、性能监控、缓存、输入校验等
编译时检查、依赖注入、事务管理、ORM 映射等
语法
使用高阶函数实现,修饰函数或类
使用元数据标注,修饰类、方法、字段等
总结
拓展 类装饰器 类装饰器的实现与函数装饰器类似,只是它接收和返回的是类对象。
类装饰器是指装饰类 ,即使用装饰器修饰一个类,而不仅仅是函数或方法。这种装饰器和函数装饰器类似,主要是为了增强类的功能,修改类的行为,或者为类添加某些功能。
当我们说“类装饰器”,一般是指作用在类上的装饰器 ,即用来修饰类的装饰器,而不是用类来实现的装饰器。类装饰器主要用于改变类的属性、方法或实例的行为,甚至可以返回一个修改过的类。
应用场景
类的属性或方法自动增强 :可以在不改变类定义的情况下,动态地修改或添加属性和方法。
类的注册机制 :将类自动注册到某个管理系统中(比如某个全局注册表)。
单例模式 :强制类只能创建一个实例。
权限控制 :为某类对象的方法添加权限检查。
类装饰器的语法
类装饰器的使用和函数装饰器非常相似,使用 @decorator_name
的语法,装饰器接受类作为参数并返回修改后的类。
1 2 3 4 5 6 7 8 def class_decorator (cls ): return cls@class_decorator class MyClass : def method (self ): pass
示例
这是一个简单的类装饰器例子,它为类动态地添加一个新的方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 def add_method_decorator (cls ): def new_method (self ): return "This is a dynamically added method!" cls.new_method = new_method return cls@add_method_decorator class MyClass : def original_method (self ): return "This is the original method." obj = MyClass()print (obj.original_method()) print (obj.new_method())
在这个例子中,装饰器 add_method_decorator
向类 MyClass
添加了一个 new_method
方法,而不需要修改类定义。装饰后的类实例可以调用这个新增的方法。
单例模式是一种常见的设计模式,它确保一个类只能有一个实例。使用类装饰器可以优雅地实现这一点。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 def singleton (cls ): instances = {} def get_instance (*args, **kwargs ): if cls not in instances: instances[cls] = cls(*args, **kwargs) return instances[cls] return get_instance@singleton class MySingletonClass : def __init__ (self, value ): self.value = value obj1 = MySingletonClass(10 ) obj2 = MySingletonClass(20 )print (obj1 is obj2) print (obj1.value) print (obj2.value)
在这个例子中,singleton
装饰器通过内部字典 instances
缓存类的实例,确保每次调用时都返回同一个实例。尽管两次使用不同的参数创建对象,装饰器强制它们共享同一个实例。
类装饰器还可以用于自动注册类,这在大型应用中非常有用,比如在插件系统中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 registry = {}def register_class (cls ): registry[cls.__name__] = cls return cls@register_class class MyClassA : pass @register_class class MyClassB : pass print (registry)
输出:
1 {'MyClassA': <class '__main__.MyClassA'>, 'MyClassB': <class '__main__.MyClassB'>}
在这个例子中,register_class
装饰器将类注册到全局的 registry
字典中,允许程序在其他地方动态地查找和使用这些类。
functools.wraps
是一个装饰器,通常用于在自定义装饰器中保持被装饰函数的元数据(如函数名、文档字符串、注释等),以免被装饰器的内部函数覆盖。它能够让装饰过的函数看起来依然像原来的函数,从而保持一致性。
例子
下面我们使用 functools.wraps
来构建一个装饰器,记录函数的调用日志,并保证装饰后的函数保留原函数的元信息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import functoolsdef log_execution (func ): @functools.wraps(func ) def wrapper (*args, **kwargs ): print (f"Executing '{func.__name__} ' with arguments {args} and keyword arguments {kwargs} " ) result = func(*args, **kwargs) print (f"'{func.__name__} ' returned {result} " ) return result return wrapper@log_execution def add (a, b ): """Returns the sum of two numbers.""" return a + b result = add(10 , 20 )print (f"Result: {result} " )print (f"Function name: {add.__name__} " )print (f"Function docstring: {add.__doc__} " )
输出:
1 2 3 4 5 Executing 'add' with arguments (10, 20) and keyword arguments {} 'add' returned 30 Result: 30 Function name: add Function docstring: Returns the sum of two numbers.
在这个例子中,log_execution
装饰器在函数执行前后打印日志,记录调用时的参数和返回值。由于我们使用了 functools.wraps(func)
,装饰后的 add
函数依然保留了原函数的名称和文档字符串,否则它们会被内部的 wrapper
函数覆盖掉。
如果不使用 functools.wraps
的效果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 def log_execution (func ): def wrapper (*args, **kwargs ): print (f"Executing '{func.__name__} ' with arguments {args} and keyword arguments {kwargs} " ) result = func(*args, **kwargs) print (f"'{func.__name__} ' returned {result} " ) return result return wrapper@log_execution def add (a, b ): return a + bprint (f"Function name: {add.__name__} " )print (f"Function docstring: {add.__doc__} " )
输出:
1 2 Function name: wrapper Function docstring: None
可以看到,如果不使用 functools.wraps
,add
函数的名称变成了 wrapper
,而且原来的文档字符串也丢失了。因此,functools.wraps
非常重要,它确保装饰后的函数保留原始函数的元数据信息。