欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Python装饰器的理解与使用教程

程序员文章站 2022-11-28 11:20:29
Python装饰器(英文decorator) 本质是函数 为其他函数添加附加功能。装饰器本身也是Python的一个重点,所以无论如何你必须弄懂它。装饰器的存在其实就是为了在需要添...

Python装饰器(英文decorator)

本质是函数 为其他函数添加附加功能。装饰器本身也是Python的一个重点,所以无论如何你必须弄懂它。装饰器的存在其实就是为了在需要添加新功能时不影响之前版本的使用的同时来增加新功能,其实也是一种“偷懒”的办法。它的使用场景较多,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景。有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。

Python装饰器的两大原则:

(1)在不修改被修饰函数源代码的同时为其增加功能

(2)不能修改被装饰的函数的调用方式

Python装饰器使用详解

装饰器的使用其实也并非很复杂,只要你理解了它的基本原理以后,你便会爱上这个方法。在这里做一个简单讲解,所有的函数本身就是一个“变量”,我们其实是可以直接把函数当作参数来进行传递的(但是在传递函数时不需要加括号,只需要函数名),其实传递函数的本质就是传递了函数的地址,拿到这个地址后加上括号就可以运行了。

(1)无参数的装饰器

import time  
  
  
# 装饰器  
def decorator(func):  # func为装饰器绑定的方法(绑定装饰器后自动传入)  
    def Deco():  
        start_time = time.time()  
        func()  # 调用test方法  
        end_time = time.time()  
        print("time:", end_time - start_time, "秒")  
  
    return Deco  # 返回Deco方法  
 
 
@decorator  # 添加装饰器  
def test():  
    time.sleep(2)  
    print("run test")  
  
  
test()  # 调用函数  

(2)有参数的装饰器

import time  
  
  
# 装饰器  
def decorator(func):  # func为装饰器绑定的方法(绑定装饰器后自动传入)  
    def Deco(arg1):  # 传入test方法的参数  
        start_time = time.time()  
        func(arg1)  # 调用test方法  
        end_time = time.time()  
        print("time:", end_time - start_time, "秒")  
  
    return Deco  # 返回Deco方法  
 
 
@decorator  # 添加装饰器  
def test(mStr):  
    time.sleep(2)  
    print("run test:"+mStr)  
  
  
test("传入参数")  # 调用函数  

(3)装饰器即可装饰带参函数也可以装饰不带参函数

import time  
  
  
# 装饰器  
def decorator(func):  # func为装饰器绑定的方法(绑定装饰器后自动传入)  
    def Deco(*arg1, **kwargs):  # (传入非固定参数)这样即使装饰函数不带参数也可被装饰   
        start_time = time.time()  
        func(*arg1, **kwargs)  
        end_time = time.time()  
        print("time:", end_time - start_time, "秒")  
  
    return Deco  # 返回Deco方法  
 
 
@decorator  # 添加装饰器  
def test():  
    time.sleep(2)  
    print("run test:")  
  
  
test()  # 调用函数  

(4)装饰器的高级使用

以上的装饰器已经可以满足基本需求了,但是他们第一存在一个问题,被装饰的函数的返回结果会被修改。

这里我们模拟一个登陆装饰器,装饰器需要对不同的被装饰函数使用不同的登陆方法,并且是返回值不被修改。

user, password = 'db', '12345'  
  
# 当装饰器也需要参入参数时我们需要给装饰器再加一层函数,  
# 此时装饰器接受到的方法需要进入第二层函数进行接受,  
# 第一层需要接受装饰器自己的参数  
  
  
def login(login_type):  
    def outer_wrapper(func):  
        def wrapper(*agr1, **kwargs):  
            usernameInput = input("UserName:").strip()  
            passwordInput = input("Password:").strip()  
            if login_type == "local":  
                if user == usernameInput and password == passwordInput:  
                    print("login successful")  
                    res = func(*agr1, **kwargs)  # 接受返回结果  
                    return res  
                else:  
                    print("login fail")  
            elif login_type == "ldap":  
                print("远程登录")  
        return wrapper  
    return outer_wrapper  
  
  
def index():  
    print("welcome to index page")  
 
 
@login(login_type="local")  # 对装饰分类  
def home():  
    print("welcome to home page")  
    return "from home"  
 
 
@login(login_type="ldap")  # 对装饰分类  
def blog():  
    print("welcome to blog page")  
  
  
index()  
print(home())  
blog()  

总结

(1) 要想装饰器不修改被装饰函数的返回值,我们需要在装饰器中接受被装饰函数的返回值并Return即可。

(2) 如果希望对被装饰函数进行分类处理,我们可以在绑定装饰器时传入一个参数用于对被装饰函数进行分类,但是这样我们需要在装饰器中在套一层函数,在第一层接收装饰器传递的参数,在第二层函数中接收被装饰函数。

(3) 如果希望装饰器既能装饰带参的函数也可以修饰不带参的函数,我们只需要在装饰器中接收参数时,把参数定义为非固定参数即可。