Python“三大器”之装饰器1

一、装饰器

1、什么是装饰器?

  装饰:装饰、修饰

  器:工具

  装饰器:装饰的工具

 

  (*****)“开放封闭”:装饰器必须要遵循“开放封闭”原则:

    开放:对函数功能的添加是开放的

    封闭:对函数功能的修改是封闭的

 

2、装饰器的作用?

  在不修改被装饰对象源代码与调用方式的前提下添加新的功能。

  装饰器必须遵循的两个原则:

    不修改被装饰对象源代码

    不修改被装饰对象的调用方式

 

ps:

  被装饰对象指的是  --->  需要添加功能的(函数)

  装饰器指的是  --->  被装饰对象添加的新功能的(函数)

 

3、为什么要使用装饰器?

  可以解决代码荣誉问题,提高代码的可扩展性

 

4、怎么使用装饰器?

  编写装饰器:

    通过闭包函数来实现装饰器

  装饰器的应用:

    1、统计时间

    2、登录认证

 

例子:

  需求:需要统计一下下载电影的时间

    方案一:函数调用(适合少次使用)

复制代码

import time

def download_movie():
    print("开始下载电影...")
    # 模拟电影下载时间为3秒
    time.sleep(3)
    print("电影下载成功!")

start_time = time.time()    # 获取当前时间戳
download_movie()
end_time = time.time()    # 获取当前时间戳
print(f"消耗时间:{end_time - start_time}")

复制代码

  问题1:如果有多个被装饰对象,需要写多次统计时间的代码,导致代码冗余,于是有了方案二。

 

    方案二:使用装饰器(适合多次调用,调用时直接在闭包函数内传入所需要使用装饰器的函数名称)

复制代码

import time

def download_movie():
    print("开始下载电影...")
    # 模拟电影下载时间为3秒
    time.sleep(3)
    print("电影下载成功!")

# 模拟多个被装饰对象
def func1():
    pass

# 模拟多个被装饰对象   
def func2():
    pass

# 装饰器:初级版
def time_record(func):
    def inner():
        # 获取当前时间戳
        start_time = time.time()
        func()
        # 获取当前时间戳
        end_time = time.time()
        print(f"消耗时间:{end_time - start_time}")
    return inner

download_movie = time_record(download_movie)
download_movie()

复制代码

  问题2:如果被装饰对象有返回值,有参数而且有多个参数,于是有了方案三。

 

    方案三:方案二的代码优化,使用*args, **kwargs接收所有参数(形参,关键字参数等)import time

复制代码

def download_movie(*args, **kwargs):
    print("开始下载电影...")
    # 模拟电影下载时间
    time.sleep(3)
    print("电影下载成功!")
    return "movie.mp4"

# 装饰器最终版
def time_record(func):
    def inner(*args, **kwargs):    # *args **kwargs接收所有参数(形参,关键字参数等)
        # 获取当前时间戳
        start_time = time.time()
        # 将被装饰对象需要接收的人以参数,原封不动的传给func(被修饰对象)
        res = func(*args, **kwargs)    #此处的fanc为被修饰对象download_movie()
        # 获取当前时间戳
        end_time = time.time()
        # 统计结束,打印统计时间
        print(f"消耗时间:{end_time - start_time}")
        return res
    return inner

# 这里的download_movie是inner()的返回值
download_movie = time_record(download_movie)
# download_movie()  --->  inner() ,可传入任意参数,若传入参数,download_movie()函数需要修改代码块,否则不打印或者不执行传入参数
download_movie()

复制代码

  方案三完美的解决了方案一和方案二遗留下的问题,此处的装饰器可以作为所有装饰器的模板

 

  (*****)装饰器模板(记牢)

 

复制代码

def wrapper(func):

    def inner(*args, **kwargs):    # *args **kwargs接收所有参数(形参,关键字参数等)

        # 调用被装饰对象,得到被装饰对象的返回值res
        res = func(*args, **kwargs)

        return res

    return inner

复制代码

 

 

  装饰器简例:

复制代码

def wrapper(func):
    def inner(*args, **kwargs):
        '''
        在此处可以添加新添加的功能代码块
        '''

        # 调用被装饰对象,得到被装饰对象的返回值res
        res = func(*args, **kwargs)

        '''
        在此处也可以添加新添加的功能代码块
        '''
        return res
    return inner

def func1():
    print("hello")

func1 = wrapper(func1)
func1()

复制代码

代码执行顺序:

  def为定义函数,不执行

  先执行同级代码,再执行下级函数体代码

 

 

回到顶部

二、装饰器语法糖

  装饰器的语法糖,是属于装饰器的(语法糖是装饰器内置的,可以引用所有的语法糖)

  @装饰器名字      装饰器的语法糖

  注意:在使用装饰器语法糖时,装饰器必须定义在被装饰对象之上

 

例子:统计函数执行时间

  不使用装饰器语法糖:

复制代码

import time

# func函数执行三秒
def func():
    time.sleep(3)

# 装饰器:统计函数执行时间
def wrapper(func):    # func被装饰对象
    def inner(*args, **kwargs):    # *args, **kwargs是被装饰对象的参数
        # 调用前增加新功能
        start_time = time.time()
        res = func(*args, **kwargs)
        # 调用前增加新功能
        end_time = time.time()
        print(f"程序执行时间为:{end_time - start_time}秒")
        return res    # 调用被装饰对象,接收返回值
    return inner

# 不使用装饰器语法糖
func = wrapper(func)
func()

复制代码

 

  使用装饰器语法糖:

复制代码

import time

# 装饰器:统计函数执行时间
def wrapper(func):    # func被装饰对象
    def inner(*args, **kwargs):    # *args, **kwargs是被装饰对象的参数
        # 调用前增加新功能
        start_time = time.time()
        res = func(*args, **kwargs)
        # 调用前增加新功能
        end_time = time.time()
        print(f"程序执行时间为:{end_time - start_time}秒")
        return res    # 调用被装饰对象,接收返回值
    return inner

# 使用装饰器语法糖:使用装饰器语法糖时,装饰器必须定义在被装饰对象之上
@wrapper    # @wrapper    就等于--->   func = wrapper(func)
# func函数执行三秒
def func():
    time.sleep(3)



func()    # 因为语法糖可直接调用

复制代码

 

回到顶部

三、装饰器练习题

  编写一个装饰器,为多个函数加上认证的功能(用户的账号密码来源于文件),要求登录成功一次,后续的函数都无需再输入用户名密码

  可写完对照

复制代码

# 定义一个字典来做判断
user_info = {
    "user" : None,    # username与user_info["user"]来判断是否已登录用户
}

# 登录功能:
def login():
    # 判断用户没有登录时,执行
    # 登陆功能
    print("请先登录")
    username = input("请输入你的用户名:").strip()
    password = input("请输入您的密码:").strip()
    # 打开密码表,对比账户密码是否正确
    with open("dir/passwd.txt", "r", encoding="utf-8") as f:
        for line in f:
            # print(line)
            name, pwd = line.strip("\n").split(":")  # 得到[tank, 123]
    if username == name and password == pwd:
        print("登陆成功!")
        user_info["user"] = username
    else:
        print("登录失败")

# 登录认证装饰器:
def login_auth(func):
    def inner(*args, **kwargs):
        # 登录认证功能
        # 如果已经登录,将被装饰对象直接调用并返回
        if user_info.get("user"):
            res = func(*args, **kwargs)
            return res
        # 如果没有登录,执行登录功能
        else:
            login()    # 调用login()函数进行登录
    return inner

# func1、2、3都需要先登录才能使用,若登陆一次,后续就不需要再次登录
# 登陆之后可以使用的功能1
@login_auth
def func1():
    print("from func1")
    pass

# 登陆之后可以使用的功能2
@login_auth
def func2():
    print("from func2")
    pass

# 登陆之后可以使用的功能3
@login_auth
def func3():
    print("from func3")
    pass


# 执行环节
while True:
    func1()
    input("延迟操作")
    func2()
    func3()

本文首发于python黑洞网,csdn同步更新

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值