Python装饰器

文章目录

  • 装饰器(decorator)
  • 1. 将函数作为参数传给另一个函数
  • 2. 第一个装饰器
  • 3. @wraps
  • 4. 装饰器模板
  • 5. 带参数的装饰器
  • 6. 装饰器类

装饰器(decorator)

装饰器(Decorators)是Python的⼀个重要部分。简单地说:他们是修改其他函数的功能的函 数。他们有助于让我们的代码更简短,也更Pythonic。

1. 将函数作为参数传给另一个函数

在Python里,函数也可以作为函数形参被传入
Python装饰器_第1张图片

2. 第一个装饰器

在上一个例子中,其实我们已经创建了一个装饰器。有一个函数 ,他接受的参数是一个函数。


Python装饰器_第2张图片

But!:上述代码中,当使用doSomethingBeforeHi(hi)的时候,没有定义一个新的函数,而是直接执行了!因此我们需要新的代码组织方式,来返回一个新的函数,方法就是在函数内定义一个函数,最后返回。

Python装饰器_第3张图片
这样就可以返回一个用装饰器装饰后的函数,要变成常见的装饰器形式@,只需要:
Python装饰器_第4张图片
装饰器装饰的函数应该是任意的,因此装饰器内a_func的参数是无法预料的,这时候我们就需要使用*args**kwargs

Python装饰器_第5张图片

output:
before a_func
I'm a_func 1
after a_func

我们可以发现,在装饰前后,a_func接受的参数是不变的,a_func_dec在被装饰前和装饰后都只接收一个int,装饰器应该是无感的。(当然也可以写成不一样的,但是不太好)

3. @wraps

@wraps(func) 是一个用于装饰器的装饰器,通常用于保留原始函数的元数据(如函数名、文档字符串等)。这是一种良好的习惯,可以确保装饰后的函数在行为上与原始函数一致,同时保留原始函数的信息。通常应该在定义自己的装饰器时使用 @wraps(func)

示例:

from functools import wraps

def decorator(a_func):
    @wraps(a_func)
    def wrapTheFunction(*args, **kwargs):
        print("before a_func")

        a_func(*args, **kwargs)

        print("after a_func")
    return wrapTheFunction

@decorator
def a_func_dec(num:int):
    print("I'm a_func", num)

a_func_dec(1)
print(a_func_dec.__name__)

output:
before a_func
I'm a_func 1
after a_func
a_func_dec

在上面的示例中,@wraps(func) 装饰器确保装饰后的函数 wrapper 保留了原始函数 a_func_dec 的函数名和文档字符串。这使得装饰后的函数在行为上更像原始函数,同时不会丢失有用的元数据信息。

在编写自定义装饰器时,使用 @wraps 是一种良好的实践,以确保你的装饰器不会破坏原始函数的元数据。

4. 装饰器模板

from functools import wraps


def decorator_name(f):
    @wraps(f)
    def wrapper(*args, **kwargs):
        return f(*args, **kwargs)
    return wrapper

@decorator_name
def func():
    return("Function is running")

print(func())

5. 带参数的装饰器

当我们使用装饰器放于一个函数前时,默认第一个参数就是这个函数,因此我们不需要再输入新的参数,但是如果有其他的参数,我们就需要像函数调用的方式写在装饰器后。

可能显而易见的会想到,能不能直接接在ecorator_name(f)的f后面,这样是可以的,但是之后就必须得使用显示声明参数,

def repeat_decorator(func, num_repeats):
    def wrapper(*args, **kwargs):
        results = []
        for _ in range(num_repeats):
            result = func(*args, **kwargs)
            results.append(result)
        return results
    return wrapper

# 使用装饰器并传递参数
@repeat_decorator(num_repeats=3)
def greet(name):
    return f"Hello, {name}!"

results = greet("Alice")
print(results)

因此我们需要别的方法来实现带参数的装饰器,一般我们使用如下方法,三层函数嵌套,最外层是装饰器的参数接收,第二层是装饰器,接收被装饰的函数,最内层是装饰完的函数,并最终被返回。
例一:

def repeat_decorator(num_repeats):
    def decorator(func):
        def wrapper(*args, **kwargs):
            results = []
            for _ in range(num_repeats):
                result = func(*args, **kwargs)
                results.append(result)
            return results
        return wrapper
    return decorator

@repeat_decorator(3)  # 使用装饰器并传递参数
def greet(name):
    return f"Hello, {name}!"

results = greet("Alice")
print(results)

例二:

from functools import wraps


def logit(logfile='out.log'):
	def logging_decorator(func):
		@wraps(func)
		def wrapped_function(*args, **kwargs):
			log_string = func.__name__ + " was called"
			print(log_string)
			# 打开logfile,并写⼊内容
			with open(logfile, 'a') as opened_file:
				# 现在将⽇志打到指定的logfile
				opened_file.write(log_string + '\n')
		return wrapped_function
	return logging_decorator

@logit()
def myfunc1():
	pass

myfunc1()
# Output: myfunc1 was called
# 现在⼀个叫做 out.log 的⽂件出现了,⾥⾯的内容就是上⾯的字符串
@logit(logfile='func2.log')
def myfunc2():
	pass

myfunc2()
# Output: myfunc2 was called

6. 装饰器类

是不是感觉这样就有点复杂了?三层函数嵌套,这时候我们就可以使用类来构建装饰器。
Python中,当调用对象名的时候,会调用__call__方法,因此我们联想到使用类来充当装饰器的效果。

class MyCallableClass:
    def __init__(self, value):
        self.value = value

    def __call__(self, x):
        return self.value * x

# 创建一个类的实例
callable_instance = MyCallableClass(5)

# 使用实例像调用函数一样
result = callable_instance(10)

print(result)  # 输出 50

类装饰器例:

class MyDecorator:
    def __init__(self, prefix):
        self.prefix = prefix

    def __call__(self, func):
        def wrapped_function(*args, **kwargs):
            result = func(*args, **kwargs)
            return f"{self.prefix}: {result}"
        return wrapped_function

# 使用类装饰器
@MyDecorator("Result")
def greet(name):
    return f"Hello, {name}!"

result = greet("Alice")
print(result)

@MyDecorator("Result")在实例化一个对象之后,对象调用__call__方法,__call__方法是一个装饰器,返回装饰后的函数。
装饰器的参数写在__init__里,装饰器的内容写在__call__内,当装饰器比较复杂的时候,使用装饰器类更加简洁明了。

你可能感兴趣的:(python,python,开发语言)