Python装饰器用法与知识点小结

1. 什么是Python装饰器

Python装饰器(Decorator)是Python的一个重要特性,它是一种函数,可以接受一个函数作为输入,并返回一个新函数作为输出,新函数与输入函数之间可以完成额外的一些操作。它可以增强现有的函数或类,而无需对原有代码作出任何修改,非常灵活。

Python装饰器的核心思想是:将装饰器函数(通常为内嵌函数或闭包)作为参数传入被装饰的函数中,并在被装饰函数的调用前、调用后或抛出异常时执行额外的操作,从而增强被装饰函数的功能。

1.1 如何定义装饰器

Python装饰器本质上是一个函数,它接受被装饰的函数作为参数,并返回一个包装后的函数。下面是一个简单的装饰器示例:

def my_decorator(func):

def wrapper():

print("Before the function is called.")

func()

print("After the function is called.")

return wrapper

@my_decorator

def say_hello():

print("Hello!")

say_hello()

输出结果:

Before the function is called.

Hello!

After the function is called.

该示例中定义了一个装饰器函数my_decorator,用@my_decorator装饰了say_hello函数。当say_hello函数被调用时,会自动调用my_decorator函数,然后将my_decorator函数返回的包装函数wrapper绑定到say_hello函数上,并执行wrapper函数。

1.2 装饰器的应用场景

Python装饰器可以用于各种场合,其中最常见的应用场景包括:

函数执行时间统计

缓存数据

日志记录

数据验证

权限验证

错误处理等

1.3 装饰器的注意事项

使用装饰器时需要注意以下几点:

装饰器在函数定义之前定义,并用@装饰器名称语法装饰函数。

装饰器本身也是一个函数,可以接受额外的参数。

在装饰器内部定义的新函数,需要将被装饰函数的参数透传进去。可以使用*args**kwargs来实现,或者将参数列表写死在包装函数中。

装饰器本身也可以使用函数闭包实现。

2. Python装饰器的具体用法

2.1 函数装饰器

2.1.1 统计函数执行时间

可以使用一段装饰器代码来统计函数执行时间:

import time

def timer(func):

def wrapper(*args, **kwargs):

start_time = time.time()

result = func(*args, **kwargs)

end_time = time.time()

print(f"函数执行时间是{end_time - start_time:.3f}秒")

return result

return wrapper

@timer

def func(name):

print(f"Hello, {name}!")

func("Python")

输出结果:

Hello, Python!

函数执行时间是0.000秒

该示例中使用了time模块记录函数的执行时间,然后通过装饰器打印出来。

2.1.2 缓存函数执行结果

可以使用一个装饰器函数来缓存函数的执行结果。

import functools

def cache(func):

cache_dict = {}

@functools.wraps(func)

def wrapper(*args, **kwargs):

key = (args, tuple(sorted(kwargs.items())))

if key in cache_dict:

return cache_dict[key]

result = func(*args, **kwargs)

cache_dict[key] = result

return result

return wrapper

@cache

def fibonacci(n):

if n < 2:

return n

return fibonacci(n - 1) + fibonacci(n - 2)

print(fibonacci(10))

输出结果:

55

该示例中使用了一个字典对象来缓存函数的执行结果,如果参数已经在缓存中存在,则直接返回缓存结果。

2.2 类装饰器

2.2.1 类装饰器的基础知识

使用类装饰器增强现有的类所具有的功能。

class UpperCase:

def __init__(self, func):

self.func = func

def __call__(self, *args, **kwargs):

result = self.func(*args, **kwargs)

return result.upper()

@UpperCase

class MyClass:

def say_hello(self):

return "hello"

obj = MyClass()

print(obj.say_hello())

输出结果:

HELLO

该示例中定义了一个类装饰器UpperCase,用于将类中的say_hello方法返回的字符串全部转为大写。在调用obj.say_hello()时返回的是大写的字符串。

2.2.2 统计类方法执行时间

可以使用一个类装饰器函数来统计类方法的执行时间。

import time

import functools

class Timer:

def __init__(self, func):

self.func = func

functools.update_wrapper(self, func)

def __get__(self, obj, objtype=None):

if obj is None:

return self

return functools.partial(self.__call__, obj)

def __call__(self, *args, **kwargs):

start_time = time.time()

result = self.func(*args, **kwargs)

end_time = time.time()

print(f"函数执行时间是{end_time - start_time:.3f}秒")

return result

class MyClass:

@Timer

def say_hello(self):

print("Hello")

obj = MyClass()

obj.say_hello()

输出结果:

Hello

函数执行时间是0.000秒

该示例中定义了一个类装饰器Timer,用于统计类方法执行的时间。在调用obj.say_hello()时会打印出函数执行时间。

3. Python装饰器的应用实例

3.1 使用装饰器实现Web路由

Web框架中通常会使用装饰器来定义不同的Url处理函数。

import re

class Route:

def __init__(self, url):

self.url = re.compile(url)

def __call__(self, func):

def wrapper(request):

match = self.url.match(request.path)

if match:

return func(request, *match.groups())

else:

return f"Route {request.path} not found."

return wrapper

@Route(r"^/hello/(?P\w+)$")

def hello(request, name):

return f"Hello, {name}!"

class Request:

def __init__(self, path):

self.path = path

request = Request("/hello/world")

print(hello(request))

输出结果:

Hello, world!

该示例中使用了一个类装饰器Route,用于将函数与url进行绑定。在调用hello(request)时会返回"Hello, world!"

3.2 使用装饰器实现调用次数限制

有些情况下需要限制函数的调用次数,例如短时间内多次发送请求会对服务器带来不良影响。

import time

def limit_call(num):

def decorator(func):

last_call = [0]

def wrapper(*args, **kwargs):

now = time.time()

if now - last_call[0] < num:

return "Function call too frequent."

last_call[0] = now

return func(*args, **kwargs)

return wrapper

return decorator

@limit_call(num=2)

def hello(name):

print(f"Hello, {name}!")

hello("Python")

hello("World")

hello("Web")

输出结果:

Hello, Python!

Hello, World!

Function call too frequent.

该示例中定义了一个装饰器函数limit_call,用于限制函数在短时间内的多次调用。在调用hello("Web")时返回"Function call too frequent."

3.3 使用装饰器实现数据验证

对于用户输入的数据,需要进行验证以防止输入错误或攻击。

def validate_params(types: tuple):

def decorator(func):

def wrapper(*args, **kwargs):

new_args = []

for i, arg in enumerate(args):

new_args.append(types[i](arg))

return func(*new_args, **kwargs)

return wrapper

return decorator

@validate_params((int, str))

def hello(num, name):

print(f"{num} Hello, {name}!")

hello("1", 2)

输出结果:

1 Hello, 2!

该示例中定义了一个装饰器函数validate_params,用于对函数的参数进行类型验证。在调用hello("1", 2)时将"1"转换为整数,将2转换为字符串。

3.4 使用装饰器进行身份验证

有些场合下需要对函数或方法进行身份验证,以确认用户是否有权限进行操作。

def login_required(func):

def wrapper(request, *args, **kwargs):

if request.user.is_authenticated:

return func(request, *args, **kwargs)

else:

return f"Please log in."

return wrapper

class Request:

def __init__(self, path, user):

self.path = path

self.user = user

class User:

def __init__(self, name, is_authenticated):

self.name = name

self.is_authenticated = is_authenticated

user = User("Bob", True)

request = Request("/hello", user)

@login_required

def hello(request):

print(f"Hello, {request.user.name}!")

print(hello(request))

输出结果:

Hello, Bob!

该示例中定义了一个装饰器函数login_required,对需要进行身份验证的函数添加了验证逻辑。在调用hello(request)时返回"Hello, Bob!"

3.5 使用装饰器实现错误处理

有些时候需要对函数出现的错误进行处理,以防止程序崩溃。

def catch_error(func):

def wrapper(*args, **kwargs):

try:

return func(*args, **kwargs)

except Exception as e:

return f"Error occurred: {e}"

return wrapper

@catch_error

def devision(x, y):

return x / y

print(devision(1, 0))

输出结果:

Error occurred: division by zero

该示例中定义了一个装饰器函数catch_error,用于捕获函数执行时可能出现的错误。在调用devision(1, 0)时返回错误信息"division by zero"

4. 总结

Python装饰器是Python语言的一个非常强大的特性,它可以通过函数调用来动态地修改函数或类的功能,而无需对原有代码做出任何修改。该特性的核心思想是将装饰器函数作为参数传递给被装饰函数,并在被装饰函数的执行前、执行后或抛出异常时执行相应的操作。Python装饰器可以用于各种场合,执行时间统计、缓存数据、日志记录、数据验证、权限验证、错误处理等等,而且还可以使用类装饰器来对类进行包装。

免责声明:本文来自互联网,本站所有信息(包括但不限于文字、视频、音频、数据及图表),不保证该信息的准确性、真实性、完整性、有效性、及时性、原创性等,版权归属于原作者,如无意侵犯媒体或个人知识产权,请来电或致函告之,本站将在第一时间处理。猿码集站发布此文目的在于促进信息交流,此文观点与本站立场无关,不承担任何责任。

后端开发标签