Python基础之装饰器

发布时间 2023-05-31 17:21:31作者: Way*yy

装饰器

1、为什么要用装饰器

1.1、为程序提供扩展功能的可能性

1.2、要遵循开放封闭原则

1.3、禁止修改原代码,但是可以新增功能

1.4、也不能修改调用方式

2、什么是装饰器

2.1为被装饰对象添加新功能的工具

2.2、不修改被装饰对象源代码和调用方式

3、装饰器的核心思想

3.1、就是在"不改变被装饰对象"内部的代码和"原有调用方式"的基础之上再"添加额外的功能"

4、装饰器的实现

实现原理:
	函数嵌套+闭包+函数对象
    

简易版本的装饰器

########################################################### 
# 背景知识
def index():
    print("from index")
index()
# 现在让你统计index函数执行完毕之后的执行时间

# 时间模块
import time  # 内置模块,可以直接拿来使用

def index():
    time.sleep(3)
    print('from index')
# 1. 在函数执行之前打印一个时间点
start_time = time.time()
index()

# 2. 在函数执行完毕之后在打印一个时间点
end_time = time.time()

# 3. 两者的差就是函数的执行时间
print("index函数一共执行了:%s秒" % (end_time - start_time))
这样一个最low逼的装饰器就做好了

# 这样的装饰器可不可以用?
	可以用但是写死了,其他的函数想用就用不了,所以我们基于上方的前提上还可以再改改
    ###########################################################
 import time
def index():
    time.sleep(6)
    print('from index')


def outer(func):
    def get_time():
        start_time = time.time()
        func()
        end_time = time.time()
        print('函数执行了%s秒' % (end_time - start_time))
    return get_time


index=outer(index)
index()

# 这样的话这个装饰器就可以实现让多个函数使用的,也不会有代码冗余问题

解决参数问题

至此我们便实现了一个无参装饰器timer,可以在不修改被装饰对象index源代码和调用方式的前提下为其加上新功能。但我们忽略了若被装饰的函数是一个有参函数,便会抛出异常。因为调用的那个index使我们自己给get_time内存地址起的一个名字,而不是我们定义的那个函数index,所以就会出现报错,针对问题我们又该如何解决呢?
import time


def index(username, name):
    time.sleep(3)
    print('from index')


def outer(func):
    def get_time(*args, **kwargs):
        start_time = time.time()
        func(*args, **kwargs)
        end_time = time.time()
        print('函数执行了%s秒' % (end_time - start_time))

    return get_time


index = outer(index)
index('tony', 'kevin')

函数的返回值问题

import time


def index():
    time.sleep(3)
    print('from index')


def home():
    time.sleep(6)
    print('from home')


def outer(func):
    def get_time(*args, **kwargs):
        start_time = time.time()
        res = func(*args, **kwargs)
        end_time = time.time()
        print('函数执行了%s秒' % (end_time - start_time))
        return res

    return get_time


index = outer(index)
res1 = index
res1()

home = outer(home)
res2 = home
res2()

# 谁调用outer谁就拿get_time返回值,谁调用get_time谁就拿res的返回值

练习(登录认证功能)

def index():
    print(5)  # 9、执行index函数
    print('from index')


def login_auth(func):
    print(6)  # 6、开始执行login_auth函数

    def auth(*args, **kwargs):
        print(7)  # 7、执行auth函数
        name = input('请输入你的名字>>>>').strip()
        pwd = input('请输入你的密码>>>>').strip()
        print(8)  # 8、 让用户输入认证的用户名和密码
        user_res = func(*args, **kwargs)  # 8、1调用index函数
        print(9)  # 10、执行完index
        if name == 'kevin' and pwd == '123':
            print(10)  # 11、通过判断条件,认证成功
            print('恭喜你登录成功')
            return user_res  # 11.1返回index内存地址给auth
        else:
            print('用户名或密码错误')
            print(11)

    print(12)  # 3、返回auth的内存地址
    return auth


print(1)  # 1、调用login_auth并且将index的内存地址赋给func
index = login_auth(index)
print(2)  # 4、将auth内存地址给index,在通过index赋值给res
res = index
print(3)
res()  # 5、调用auth函数
print(4)

装饰器模版

def outer(func):
    def inner(*args, **kwargs):
        print('函数执行前可操作的功能')
        uer_inner = func(*args, **kwargs)
        print('函数执行后可操作的功能')
        return uer_inner

    return inner

res = outer('')

Eg:做一个函数执行时间的装饰器
   
import time


def outer(func):
    def inner(*args, **kwargs):
        start_time = time.time()
        time.sleep(3)
        uer_inner = func(*args, **kwargs)
        end_time = time.time()
        print(f'函数执行了{end_time - start_time}秒')
        return uer_inner

    return inner


def index():
    print('》》》》index')


index = outer(index)
index()

装饰器的语法糖

"""
	1、语法糖其实就是一个装饰器,装饰器本质就是函数,函数其实就是一个个的功能!
	2、语法糖的书写规范:
		紧贴着被装饰对象的上方写
	3、语法糖的内部原理:
		它会自动把下面的被装饰对象的函数名当成参数自动传递调用装饰器
	4、如果语法糖执行完毕之后,没有其他语法糖了,我们要使用跟原函数同名的变量名命名
"""
"""装饰器本质就是函数,函数就是功能"""

# 语法糖
import time


def outer(func):
    def inner(*args, **kwargs):
        start_time = time.time()
        time.sleep(3)
        uer_inner = func(*args, **kwargs)
        end_time = time.time()
        print(f'函数执行了{end_time - start_time}秒')
        return uer_inner

    return inner


@outer  # 这个语法糖就相当于index = outer(index),我们只需要在下面直接用函数名加括号调用即可
def index():
    print('》》》》index')


index()  # inner()

双层语法糖

import time


def outer(func):
    def inner(*args, **kwargs):
        start_time = time.time()
        time.sleep(3)
        uer_inner = func(*args, **kwargs)
        end_time = time.time()
        print(f'函数执行了{end_time - start_time}秒')
        return uer_inner

    return inner


def user_auth(func):
    def login(*args, **kwargs):
        name = input('请输入系统管理员账号>>>>')
        pwd = input('请输入系统管理员密码>>>')
        if name == 'root' and pwd == '000000':
            print('欢迎进入管理员系统')
            user_login = func(*args, **kwargs)
            return user_login
        else:
            print('输入的信息有误')

    return login


@user_auth
@outer  # 这个语法糖就相当于index = outer(index),我们只需要在下面直接用函数名加括号调用即可
def index():
    print('》》》》index')


index()  # inner()

# 检测是自下而上检测的,执行是从上往下执行的

多用户登录认证

import time


def outer(func):
    def inner(*args, **kwargs):
        start_time = time.time()
        time.sleep(3)
        uer_inner = func(*args, **kwargs)
        end_time = time.time()
        print(f'函数执行了{end_time - start_time}秒')
        return uer_inner

    return inner


users_login = {'login': False}


def user_auth(func):
    def login(*args, **kwargs):
        if users_login.get('users_login'):
            user_login = func(*args, **kwargs)
            return user_login
        name = input('请输入系统管理员账号>>>>').strip()
        pwd = input('请输入系统管理员密码>>>').strip()
        if name == 'root' and pwd == '000000':
            print('欢迎进入管理员系统')
            user_login = func(*args, **kwargs)
            users_login['users_login'] = True
            return user_login
        else:
            print('输入的信息有误')

    return login


@user_auth
@outer  
def index():
    print('》》》》index')


@user_auth
@outer
def home():
    print('>>>>>home')


home()
index()  # inner()

'''
这段代码定义了一个字典变量`users_login`,用于存储用户登录状态。初始状态为`False`,表示用户未登录。

然后定义了一个装饰器函数`user_auth`,它接受一个函数作为参数,并返回一个新的函数`login`。`login`函数首先检查`users_login`字典中的`users_login`键的值是否为`True`,如果是,则说明用户已经登录,直接调用原始函数并返回结果。

如果`users_login`字典中的`users_login`键的值为`False`,则说明用户未登录,需要输入管理员账号和密码进行验证。如果输入的管理员账号和密码正确,则打印欢迎信息,调用原始函数并返回结果,并将`users_login`字典中的`users_login`键的值设置为`True`,表示用户已经登录。

如果输入的管理员账号和密码不正确,则打印错误信息。最后,返回新的`login`函数。
'''

装饰器的修复技术了解

'''
	关键代码:
			from functools import wraps
			@wraps(func
'''


from functools import wraps

def outer(func):
    # func = index
    @wraps(func)
    def get_time(*args, **kwargs):
        # func:index
        # 1. 在函数执行之前打一个时间点
        start_time = time.time()
        res = func(*args, **kwargs)  # index()  func('tony')
        # 2. 在函数执行完毕之后在打一个时间点
        end_time = time.time()

        # 3. 两个时间的差值就是函数的实际执行时间
        print("函数执行了:%s秒" % (end_time - start_time))
        return res

    return get_time

有参装饰器

# 有参装饰器就是带参数的装饰器
# 先前我们学习的装饰器就是无参装饰器:不到参数的装饰器
import time


def outer(func):
    def inner(*args, **kwargs):
        start_time = time.time()
        time.sleep(3)
        uer_inner = func(*args, **kwargs)
        end_time = time.time()
        print(f'函数执行了{end_time - start_time}秒')
        return uer_inner

    return inner


users_login = {'login': False}


def user(type):
    def user_auth(func):
        def login(*args, **kwargs):
            if users_login.get('users_login'):
                user_login = func(*args, **kwargs)
                return user_login
            name = input('请输入系统管理员账号>>>>').strip()
            pwd = input('请输入系统管理员密码>>>').strip()
            if type == 'fill':
                print('这个用户名和密码来自于文件')
            elif type == "MySQL":
                print('这个用户名和密码来自于MySQL')
            if name == 'root' and pwd == '000000':
                print('欢迎进入管理员系统')
                user_login = func(*args, **kwargs)
                users_login['users_login'] = True
                return user_login
            else:
                print('输入的信息有误')

        return login

    return user_auth


@user('fill')
@outer
def index():
    print('》》》》index')


index()


@user('MySQL')
@outer
def home():
    print('>>>>>home')


home()

'''
	这段代码实现了一个装饰器函数`outer`和一个装饰器函数`user`,用于统计函数执行的时间和用户登录验证。

`outer`函数接受一个函数作为参数,并返回一个新的函数`inner`。`inner`函数使用`time`模块和`time.sleep`函数,记录函数执行前后的时间差并打印执行时间。然后调用原始函数并返回结果。

`user`函数接受一个参数`type`,用于指定登录验证方式。`user`函数返回一个装饰器函数`login`,它接受一个函数作为参数,并返回一个新的函数`login_auth`。

`login_auth`函数实现了用户登录验证功能,首先判断用户是否已经登录,如果已经登录,则直接调用原始函数并返回结果。否则,根据`type`的值选择不同的验证方式,比如从文件、MySQL中读取用户名和密码等。如果验证通过,则打印欢迎信息,调用原始函数并返回结果。

在示例中,分别使用不同的用户名和密码验证方式装饰了两个函数`index`、`home`,并在装饰器上添加了`outer`装饰器,实现了函数执行时间的统计。
'''