python装饰器

2023-12-13 23:44:32

【一】装饰器是什么?

???????在 Python 中,装饰器是一种特殊的语法结构,用于修改或增强函数或类的功能。它们可以将一个函数或类作为参数传递给另一个函数,并返回一个新的函数或类,以修改原始函数或类的行为。

装饰器通常用于以下场景:

  1. 记录函数执行时间
  2. 检查用户权限
  3. 缓存函数结果
  4. 添加日志记录
  5. 增加函数重试机制
  6. 实现单例模式等

示例:? ? ? #就是代表的运行结果

def my_decorator(func):
    def wrapper():
        print("代码执行前的函数参数.")
        func()
        print("代码执行后的函数参数.")
    return wrapper

@my_decorator
def say_hello():
    print("Hello!")

say_hello()


#代码执行前的函数参数.
#Hello!
#代码执行后的函数参数.

?????????在这个例子中,我们定义了一个名为 my_decorator 的装饰器函数,它将一个函数作为参数。装饰器函数中定义了一个名为 wrapper() 的嵌套函数,它在调用被装饰函数之前和之后打印一些信息。最后,装饰器函数返回这个嵌套函数。

????????使用 @my_decorator 语法将 say_hello() 函数传递给 my_decorator 装饰器进行装饰。这意味着当我们调用 say_hello() 函数时,实际上是调用了 wrapper() 函数。

【二】 装饰器的分类

主要分为两种:有参装饰器和无参装饰器

二者的实现原理一样,都是’函数嵌套+闭包+函数对象’的组合使用的产物。

【三】 什么时候需要装饰器
?

1. 什么时候需要用装饰器?

试想你有一系列函数,例如如下所示的两个函数(分别计算和与积):

def my_sum_function(*args):
    print("The sum is", sum(args))

def my_product_function(*args):
    res = 1
    for x in args:
        res *= x
    print("The product is", res)

my_sum_function(1, 2, 3, 4)
my_product_function(1, 2, 3, 4, 5)

现在,你需要为这一系列函数添加一个一样的功能,就是统计输入参数的个数,同时检查里面是否有0,那么低阶的解决方法就是为每一个函数单独添加部分代码,即

def my_sum_function(*args):
    print("No.args is", len(args))                # 重复部分
    contain_zero = any([x == 0 for x in args])              # 重复部分
    print(" 输入参数包含0:", contain_zero)       # 重复部分

    print("The sum is", sum(args))

def my_product_function(*args):
    print("No.args is", len(args))                # 重复部分
    contain_zero = any([x == 0 for x in args])              # 重复部分
    print("输入参数包含 0:", contain_zero)       # 重复部分

    res = 1
    for x in args:
        res *= x
    print("The product is", res)

my_sum_function(1, 2, 3, 4)
my_product_function(0, 1, 2, 3, 4, 5)

这样写的话,代码中存在大量重复的部分,有没有好一点的办法呢?也许你已经想到了,就是把添加的这部分功能封装成一个单独的函数,然后让每一个函数单独调用这个函数(嵌套调用),这样就能减少写代码过程中的“Ctrl+C和Ctrl+V”,即

def my_sum_function(*args):
    additional_function(*args)    # 嵌套调用

    print("The sum is", sum(args))

def my_product_function(*args):
    additional_function(*args)    # 嵌套调用

    res = 1
    for x in args:
        res *= x
    print("The product is", res)

def additional_function(*args):
    print("No.args is", len(args))                # 重复部分
    contain_zero = any([x == 0 for x in args])              # 重复部分
    print("输入参数包含 0:", contain_zero)  

my_sum_function(1, 2, 3, 4)
my_product_function(0, 1, 2, 3, 4, 5)

那有没有更pythonic的写法呢?这里就可以用到装饰器了,实现方法如下

def a_decorator(f):
    def additional_function(*args):
        print("No.args is", len(args))                # 重复部分
        contain_zero = any([x == 0 for x in args])              # 重复部分
        print("输入参数包含 0:", contain_zero)  
        f(*args)
    return additional_function

@a_decorator
def my_sum_function(*args):
    print("The sum is", sum(args))

@a_decorator
def my_product_function(*args):
    res = 1
    for x in args:
        res *= x
    print("The product is", res)

my_sum_function(1, 2, 3, 4)
my_product_function(0, 1, 2, 3, 4, 5)

2. 装饰器的执行机制

现在我么已经知道了装饰器的使用场景:就是为函数定制化额外功能的时候,可是添加装饰器。使用装饰器可以使代码更加简洁。那么装饰器是如何工作的呢?我们以如下例子说明:

def a_decorator(f):                         # 函数作为参数被传入
    print(f"Function {f.__name__} is passed as the augument!")
    def additional_function(*args):         # 函数嵌套
        print("No.args is", len(args))                # 重复部分
        contain_zero = any([x == 0 for x in args])              # 重复部分
        print("输入参数包含 0:", contain_zero)  
        f(*args)    # 最终执行目标函数my_sum_function的地方
    return additional_function  # 函数作为返回对象

@a_decorator
def my_sum_function(*args):
    print("The sum is", sum(args))

my_sum_function(1, 2, 3, 4)



# Function my_sum_function is passed as the augument!
# No.args is 4
# 输入参数包含 0: False
# The sum is 10

在进一步解释运行机制之前,我们需要明确几个问题:

  • 一、在Python中,一切皆对象,包括函数。一个函数可以作为另一个函数的参数,一个函数也可以作为另一个函数的返回对象;
  • 二、如果在一个函数体中定义了另一个函数,称为函数嵌套,前者称为enclosing function,后者称为enclosed function或者nested function

根据运行结果,我们可以反推出装饰器的工作机制:

  • my_sum_function作为参数传入a_decorator函数,并开始执行,因此首先打印出“Function my_sum_function is passed as the augument!”
  • 随后a_decorator函数执行过程中返回了additional_function函数对象,然后开始执行additional_function(1, 2, 3, 4),于是打印出“No. of input args is 4”和“Input arguments contain 0: False”
  • 在additional_function函数嵌套调用了my_sum_function函数,因此最后打印“The sum is 10”

也就是说,上述例子如果不用@符号,和下面是完全等价的

def a_decorator(f):
    print(f"Function {f.__name__} is passed as the augument!")
    def additional_function(*args):
        print("No.args is", len(args))                # 重复部分
        contain_zero = any([x == 0 for x in args])              # 重复部分
        print("输入参数包含 0:", contain_zero)  
        f(*args)    # 最终执行目标函数my_sum_function的地方
        
    return additional_function

def my_sum_function(*args):
    print("The sum is", sum(args))

a_decorator(my_sum_function)(1, 2, 3, 4)    # 注意理解这一行

【四】无参装饰器

无参装饰器模板

def outter(func):
    def wrapper(*args,**kwargs):
        #1 调用原函数
        #2 为其增加新功能
        res=func(*args,**kwargs)
        return res
    return wrapper

?示例:

import time
# 装饰器
def timmer(func):
    def wrapper(*args,**kwargs):
        start=time.time()
        res=func(*args,**kwargs)
        stop=time.time()
        print(stop-start)
        return res
    return wrapper

这就不能在提python自带的一种机制了语法糖?:

在被装饰对象正上方的单独一行写@装饰器名称
@timmer  ##index=timmer(index)
def index(x,y,z):
    time.sleep(2)
    print('index %s %s %s ' %(x,y,z))
@timmer ##home=timmer(123)
def home(name):
    time.sleep(2)
    print('welcome %s to home page' %name)
index(x=1,y=2,z=3)
home('jobin')

?

输出结果:?

index 1 2 3 
2.0001492500305176
welcome jobin to home page
2.0004379749298096

其中

????????????????????????@timme ==? home = timmer(home)

我们还可以按照位置传值


def outer(func):
    def inner(*args):
        print(f"装饰器的args内容>>>{args}")
        name = "tom"
        age = "12"
        res = func(name, age)
         # res = func(*args)
         # res = func(name, age, *args)
        return res
     return inner
@outer
def func(*args):
    print(f"func中args中的内容>>>{args}")
func("lucy", "15")

#装饰器的args内容>>>('lucy', '15')
#func中args中的内容>>>('tom', '12')

【五】有参装饰器?

def 有参装饰器(x,y,z):
    def outter(func):
        def inner(*args,**kwargs):

            res = func(*args,**kwargs)

            return res
        return inner
    return outter

@有参装饰器(1,y=2,x=3)
def 被装饰对象():
    pass
被装饰对象()

?上点难度:

def login(func):
    def inner(*args, **kwargs):
        # 输入用户名和密码
        username = input('请输入用户名:')
        password = input('请输入密码:')

        # 如果是管理员账户,则直接登录成功
        if username == 'admin' and password == '12186':
            print('欢迎尊敬的管理员')

        else:
            # 否则尝试从文件中读取用户记录
            with open('01.txt', 'r', encoding='utf-8') as f:
                for line in f:
                    values = line.strip().split(":")
                    if len(values) < 3:
                        print(f'记录格式不正确:{line}')
                        continue

                    # 匹配用户名和密码
                    new_username, new_password, age, gender, bankcard, withdraw_password, balance = values
                    if username == new_username and password == new_password:
                        print('用户登录成功')
                        return func( *args, **kwargs)

                print('用户名或密码错误')
                return False

    return inner

@login
def test(admin = False):
    if admin:
        print('欢迎尊贵的管理员')
    else:
        print('欢迎平民')

# 调用 test 函数
test()

总体来说,这段代码的执行流程如下:

  1. 定义装饰器?login,该装饰器接收一个函数作为参数,并返回一个新的内部函数?inner
  2. 在?inner?函数中,首先要求用户输入用户名和密码。
  3. 如果用户名和密码与管理员账户相同,则直接登录成功。
  4. 否则,尝试从文件中读取用户记录,并匹配用户名和密码。
  5. 如果用户名和密码匹配成功,则调用原始的?test?函数并返回其结果。
  6. 如果用户名和密码不正确,则打印错误信息并返回 False。
  7. 定义被装饰的函数?test,该函数默认不是管理员账户,并根据不同情况输出欢迎信息。
  8. 调用?test?函数时,会被装饰器?login?拦截并执行相关逻辑。

?

代码多敲,薪资低不了

文章来源:https://blog.csdn.net/m0_58310590/article/details/134981240
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。