📜  Python装饰器

📅  最后修改于: 2020-10-28 01:24:43             🧑  作者: Mango

Python装饰器

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

“在装饰器中,将函数作为参数传递给另一个函数,然后在包装函数内部函数。”

也称为元编程,其中程序的一部分尝试在编译时更改程序的另一部分。

在了解Decorator之前,我们需要了解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

在上面的程序中,子函数的声明方式无关紧要。子函数的执行会影响输出。这些子函数在本地受func()限制,因此不能单独调用它们。

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

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

在上面的程序中,我们已将dec()函数和inc()函数作为运算符()函数的参数传递了。

一个函数可以返回另一个函数。考虑以下示例:

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轻松使用装饰器。有时它称为“派”语法。

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来解决此错误。修改decorator.py如下:

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定义了类中未连接到类的任何其他实例的方法。 @property通常用于修改类属性的获取器和设置器。让我们通过以下示例了解它:

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

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装饰器-@staticmethod用于在类中定义静态方法。通过使用类名以及该类的实例来调用它。考虑以下代码:

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处理装饰器的参数。

注意:带参数的装饰器在编程中并不经常使用,但是它提供了灵活性。我们可以在有或没有参数的情况下使用它。

有状态的装饰者

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

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_calls中存储的函数的调用数。当我们调用say_hello()时,它将显示该函数的调用编号。

类作为装饰器

类是维护状态的最佳方法。在本节中,我们将学习如何使用类作为装饰器。在这里,我们将创建一个包含__init __()的类,并以func作为参数。该类必须是可调用的,以便它可以代表修饰的函数。

为了使一个类可调用,我们实现了特殊的__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 __()方法存储对该函数的引用,并且可以执行任何其他所需的初始化。