Python 装饰器

装饰器是 Python 最有用和最强大的工具之一。这些用于修改函数的行为。装饰器提供了包装另一个函数的灵活性,以扩展包装函数的工作,而无需永久修改它。

在 Decorators 中,函数作为参数传递给另一个函数,然后在包装函数中调用。

它也被称为meta programming,程序的一部分试图在编译时改变程序的另一部分。

在理解装饰器之前,我们需要了解 Python 的一些重要概念。

Python 中有哪些函数?

Python 有一个最有趣的特性,那就是所有的东西都被当作一个对象,甚至我们在 Python 中定义的类或者任何变量也被假定为一个对象。函数是 Python 中的第一类对象,因为它们可以引用、传递给变量以及从其他函数返回。示例如下:


def func1(msg):
print(msg)
func1("Hii")
func2 = func1
func2("Hii")

输出:

Hii
Hii

在上面的程序中,当我们运行代码时,它为两个函数提供了相同的输出。func2 指功能func1 并作为功能。我们需要理解函数的以下概念:

  • 该函数可以被引用并传递给变量,也可以从其他函数返回。

  • 这些函数可以在另一个函数中声明,并作为参数传递给另一个函数。

内部函数

Python 提供了在另一个函数中定义函数的工具。这些类型的函数称为内部函数。考虑以下示例:


def func():
print("We are in first function")
def func1():
print("This is first child function")
def func2():
print(" This is second child function")
func1()
func2()
func()

输出:

We are in first function
This is first child function
This is second child function

在上面的程序中,子函数是如何声明的并不重要。子函数的执行会对输出产生影响。这些子函数与**函数()**局部有界,因此不能单独调用。

接受其他函数作为自变量的函数也称为高阶函数。考虑以下示例:


def add(x):
return x+1
def sub(x):
return x-1
def operator(func, x):
temp = func(x)
return temp
print(operator(sub,10))
print(operator(add,20))

输出:

9
21

在上面的程序中,我们已经将 sub() 函数和 add() 函数作为参数传递给了 operator() 函数。

一个函数可以返回另一个函数。考虑下面的例子:


def hello():
def hi():
print("Hello")
return hi
new = hello()
new()

输出:

Hello

在上面的程序中, hi() 函数嵌套在 hello() 函数中。每次我们呼叫 hi() 它都会返回。

用参数装饰函数

让我们举一个例子来理解参数化装饰函数:


def divide(x,y):
print(x/y)
def outer_div(func):
def inner(x,y):
if(x

输出:

2.0

语法装饰器

在上面的程序中,我们已经修饰了 out_div() 有点笨重。Python 不使用上述方法,而是允许用@symbol轻松使用装饰器。有时它被称为“pie”语法。


def outer_div(func):
def inner(x,y):
if(x

输出:

2.0

重用装饰器

我们也可以通过调用装饰函数来重用装饰器。让我们把装饰器做成它自己的模块,可以用在许多其他功能中。使用以下代码创建名为 mod_decorator.py 的文件:


def do_twice(func):
def wrapper_do_twice():
func()
func()
return wrapper_do_twice

我们可以在其他文件中导入 mod_decorator.py。


from decorator import do_twice
@do_twice
def say_hello():
print("Hello There")
say_hello()

输出:

Hello There
Hello There

带参数的 Python 装饰器

我们想在函数中传递一些参数。让我们用下面的代码来实现:


from decorator import do_twice
@do_twice
def display(name):
print(f"Hello {name}")
display()

输出:

TypeError: display() missing 1 required positional argument: 'name'

我们可以看到,函数没有接受这个参数。运行此代码会引发错误。我们可以通过在内包装函数中使用 args 和 **kwargs 来修复这个错误。如下修改装饰器:*


def do_twice(func):
def wrapper_function(*args,**kwargs):
func(*args,**kwargs)
func(*args,**kwargs)
return wrapper_function

现在 wrapper_function() 可以接受任意数量的参数,并将它们传递给函数。


from decorator import do_twice
@do_twice
def display(name):
print(f"Hello {name}")
display("John")

输出:

Hello John
Hello John

从修饰函数返回值

我们可以控制修饰函数的返回类型。示例如下:


from decorator import do_twice
@do_twice
def return_greeting(name):
print("We are created greeting")
return f"Hi {name}"
hi_adam = return_greeting("Adam")

输出:

We are created greeting
We are created greeting

高级装饰师

让我们通过以下话题来了解一下高级装饰师:

阶级装饰者

Python 提供了两种修饰类的方法。首先,我们可以在一个类里面修饰方法;Python 中有像 @classmethod、@staticmethod@property 这样的内置装饰器。 @classmethod@staticmethod 定义了类内不与类的任何其他实例连接的方法。@属性通常用于修改类属性的 getters 和 setters。让我们通过下面的例子来理解它:

示例:1-@属性装饰器 -通过使用它,我们可以使用类函数作为属性。考虑以下代码:


class Student:
def __init__(self,name,grade):
self.name = name
self.grade = grade
@property
def display(self):
return self.name + " got grade " + self.grade

stu = Student("John","B")
print("Name:", stu.name)
print("Grade:", stu.grade)
print(stu.display)

输出:

Name: John
Grade: B
John got grade B

示例:2-@staticmethod decorator-该@ static method 用于定义类中的一个静态方法。它通过使用类名和类的实例来调用。考虑以下代码:


class Person:
@staticmethod
def hello():
print("Hello Peter")
per = Person()
per.hello()
Person.hello()

输出:

Hello Peter
Hello Peter

单例类

单例类只有一个实例。Python 中有很多单例,包括 True、None 等。

嵌套装饰者

我们可以通过将多个装饰器放在一起来使用它们。让我们考虑以下示例:


@function1
@function2
def function(name):
print(f "{name}")

在上面的代码中,我们通过将嵌套装饰器堆叠在一起来使用它们。

带参数的装饰器

在装饰器中传递参数总是有用的。根据给定的参数值,装饰器可以执行几次。让我们考虑下面的例子:


Import functools

def repeat(num):

#Creating and returning a wrapper function
def decorator_repeat(func):
@functools.wraps(func)
def wrapper(*args,**kwargs):
for _ in range(num):
value = func(*args,**kwargs)
return value
return wrapper
return decorator_repeat

#Here we are passing num as an argument which repeats the print function
@repeat(num=5)
def function1(name):
print(f"{name}")

输出:

JavatPoint
JavatPoint
JavatPoint
JavatPoint
JavatPoint

在上例中, @repeat 指的是可以在另一个函数中调用的函数对象。 @repeat(num = 5) 将返回一个充当装饰器的函数。

上面的代码看起来可能很复杂,但它是最常用的装饰器模式,我们使用了一个额外的 def 来处理装饰器的参数。

注意:带参数的 Decorator 在编程中并不常用,但它提供了灵活性。不管有没有争论,我们都可以使用它。

有状态装饰器

有状态装饰器用于跟踪装饰器状态。让我们考虑这样一个例子,我们正在创建一个装饰器,计算函数被调用的次数。


Import functools

def count_function(func):
@functools.wraps(func)
def wrapper_count_calls(*args, **kwargs):
wrapper_count_calls.num_calls += 1

print(f"Call{wrapper_count_calls.num_calls} of {func.__name__!r}")
return func(*args, **kwargs)

wrapper_count_calls.num_calls = 0
return wrapper_count_calls

@count_function
def say_hello():
print("Say Hello")

say_hello()
say_hello()

输出:

Call 1 of 'say_hello'
Say Hello
Call 2 of 'say_hello'
Say Hello

在上述程序中,状态表示存储在中的函数的调用次数。num _ 调用包装函数上的。当我们调用 say_hello() 时,它会显示该函数的调用次数。

装饰类

类是保持状态的最佳方式。在这一节中,我们将学习如何使用类作为装饰器。这里我们将创建一个包含 init() 的类,并将函数作为参数。该类需要是可调用的,这样它就可以代替修饰函数。

为了使类可调用,我们实现了特殊的 call() 方法。


import functools

class Count_Calls:
def __init__(self, func):
functools.update_wrapper(self, func)
self.func = func
self.num_calls = 0

def __call__(self, *args, **kwargs):
self.num_calls += 1
print(f"Call{self.num_calls} of {self.func.__name__!r}")
return self.func(*args, **kwargs)

@Count_Calls
def say_hello():
print("Say Hello")

say_hello()
say_hello()
say_hello()

输出:

Call 1 of 'say_hello'
Say Hello
Call 2 of 'say_hello'
Say Hello
Call 3 of 'say_hello'
Say Hello

init() 方法存储对该函数的引用,并且可以进行任何其他所需的初始化。

原文:https://www.javatpoint.com/python-decorator