Python - 装饰器

Python - 装饰器 首页 / Python3入门教程 / Python - 装饰器

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

它也称为元编程(meta programming),其中一部分程序尝试在编译时更改另一部分程序。

在理解 Decorator 之前,无涯教程需要了解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

在上面的程序中,已将 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<y):  
            x,y = y,x  
           return func(x,y)  
     return inner  
divide1 = outer_div(divide)  
divide1(2,4)  

输出:

2.0

Syntactic装饰器

在上面的程序中,无涯教程装饰了 out_div()有点笨重。代替使用上述方法,Python允许通过轻松地使用装饰器。有时它称为"pie"语法。

def outer_div(func):  
    def inner(x,y):  
        if(x < y):  
           x,y = y,x  
          return func(x,y)  
     return inner  
# syntax of generator  
@outer_div  
def divide(x,y):  
     print(x/y)   

输出:

2.0

Reusing装饰器

还可以通过调用该装饰器函数来重用该装饰器。将装饰器制作成可以在许多其他函数中使用的模块。使用以下代码创建名为 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

带参数装饰器

想在函数中传递一些参数。在以下代码中进行操作:

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("Learnfk")

输出:

Hello Learnfk
Hello Learnfk

从装饰函数返回值

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

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装饰器-通过使用它,可以将类函数用作属性。考虑以下代码:

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("Learnfk","B")
print("Name:", stu.name)
print("Grade:", stu.grade)
print(stu.display)

输出:

Name: Learnfk
Grade: B
Learnfk got grade B

示例2- @staticmethod装饰器-@staticmethod用于在类中定义静态方法。通过使用类名以及该类的实例来调用它。

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

输出:

Hello Peter
Hello Peter

嵌套装饰器

可以通过将多个装饰器彼此叠加使用来使用它们。

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

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

无涯教程网

链接:https://www.learnfk.comhttps://www.learnfk.com/python3/python-decorator.html

来源:LearnFk无涯教程网

带参数的装饰器

在装饰器中传递参数总是有用的。装饰器可以根据参数的给定值执行多次。

Import functools

def repeat(num):

#创建和返回包装函数
    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

#在这里,无涯教程将 num 作为重复打印功能的参数传递
@repeat(num=5)
def function1(name):
     print(f"{name}")

输出:

LearnFK
LearnFK
LearnFK
LearnFK
LearnFK

在上面的示例中, @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 __()方法存储对该函数的引用,并且可以执行任何其他所需的初始化。

祝学习愉快!(内容编辑有误?请选中要编辑内容 -> 右键 -> 修改 -> 提交!)

技术教程推荐

技术领导力实战笔记 -〔TGO鲲鹏会〕

技术管理实战36讲 -〔刘建国〕

TypeScript开发实战 -〔梁宵〕

摄影入门课 -〔小麥〕

软件设计之美 -〔郑晔〕

重学线性代数 -〔朱维刚〕

成为AI产品经理 -〔刘海丰〕

说透数字化转型 -〔付晓岩〕

零基础GPT应用入门课 -〔林健(键盘)〕

好记忆不如烂笔头。留下您的足迹吧 :)