装饰器!!!


从简单到复杂的装饰器

1. 装饰器简易版本

给装饰器添加统计函数执行时间的功能。

import time

def index():
	time.sleep(2)
	print('赌场开业,在线发牌')

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

index = outer(index)  # 保证不改变原有的调用方式
index()  # 实际上此时执行的是get_time函数

2. 装饰器进阶版本

  • 解决参数问题
import time

def index():
	time.sleep(1)
	print('赌场开业,在线发牌')

def login(name):
	time.sleep(1)
	print('{}正在发牌'.format(name))

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

login = outer(login)
login('jason')

  • 解决返回值问题
import time

def index():
	time.sleep(2)
	print('赌场开业,在线发牌')
	return 'from index'

def login(name):
	time.sleep(1)
	print('{}正在发牌'.format(name))
	return 'from login'

def outer(func):
	def get_time(*args, **kwargs):
		start_time = time.time()
		res = func(*args, **kwargs)  # 接收被装饰函数的返回值
		end_time = time.time()
		print('函数执行了:{}'.format(end_time - start_time))
		return res  # 返回被装饰函数执行之后的返回值
	return get_time

index = outer(index)
ret = index()
print(ret)

login = outer(login)
ret = login('jason')
print(ret1)

3. 装饰器练习(用户认证功能)

import time


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


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


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


# 构造判断用户是否已经登录的文件
is_login = {'is_login': False}


def outer(func):
    def inner(*args, **kwargs):
        # 认证功能写这里
        # 先判断用户是否已经登录了,避免重复登录
        if is_login.get('is_login'):  # 确认用户已经登录,则直接执行被装饰函数
            res = func(*args, **kwargs)
            return res
        # 用户未登录,获取用户输入的用户名和密码
        username = input('用输入用户名>>>:').strip()
        pwd = input('用输入密码>>>:').strip()
        if username == 'jason' and pwd == '123':
            res = func(*args, **kwargs)
            # 更改认证用户登录状态
            is_login['is_login'] = True
            return res
        else:
            print('用户名或密码有误,无法执行 {}'.format(func))

    return inner


home = outer(home)
home()

index = outer(index)
index()

register = outer(register)
register()

4. 装饰器固定模板(你们想要的)

def outer(func):
	def inner(*args, **kwargs):
		print('执行函数之前可以添加的额外功能')
		res = func(*args, **kwargs)
		print('执行函数之后可以添加的额外功能')
		return res  # 返回被装饰函数执行后的返回值
	return inner

def index():
	print('from index')
	
index = outer(index)
index()

5. 装饰器语法糖

def outer(func):
	def inner(*args, **kwargs):
		print('前')
		res = func(*args, **kwargs)
		print('后')
		return res
	return inner


@outer  # 相当于:index = outer(index)
def index():
	print('from index')

index()

"""
装饰器语法糖书写规范
	语法糖必须紧贴在被装饰函数对象的上方

装饰器语法糖内部原理
	会自动将下面紧贴者的被装饰对象名字当作参数传给装饰器函数调用
"""

6. 装饰器语法糖实际案例(双层)

import time

# 统计函数执行时间装饰器
def timer(func):
    def inner(*args, **kwargs):
        start_time = time.time()
        res = func(*args, **kwargs)
        end_time = time.time()
        print('函数执行时间:{}'.format(end_time - start_time))
        return res
    return inner


# 校验用户登录装饰器
# 构造判断用户是否已经登录的文件
is_login = {'is_login': False}


def outer(func):
    def inner(*args, **kwargs):
        # 认证功能写这里
        # 先判断用户是否已经登录了,避免重复登录
        if is_login.get('is_login'):  # 确认用户已经登录,则直接执行被装饰函数
            res = func(*args, **kwargs)
            return res
        # 用户未登录,获取用户输入的用户名和密码
        username = input('用输入用户名>>>:').strip()
        pwd = input('用输入密码>>>:').strip()
        if username == 'jason' and pwd == '123':
            res = func(*args, **kwargs)
            # 更改认证用户登录状态
            is_login['is_login'] = True
            return res
        else:
            print('用户名或密码有误,无法执行 {}'.format(func))

    return inner


@outer  # index = outer(timer的返回值inner)
@timer  # timer的返回值inner = timer(真正的index)
def index():
	time.sleep(1)
	print('from index')

index()
# 语法糖装饰顺序从下往上,遇到最后一个才会使用与被装饰函数名相同的变量名

"""要搞懂双层语法糖的顺序"""

7. 装饰器修复技术

将装饰器修饰的没有瑕疵

import functools

def outer(func):
	@functools.wraps(func)
	def inner(*args, **kwargs):
		print('执行函数之前添加的功能')
		res = func(*args, **kwargs)
		print('执行函数之后添加的功能')
		return res
	return inner

@outer
def index():
	print('from index')

# 此时 是真正的index
help(index)
'''Help on function index in module __main__:

index()'''

8. 装饰器语法糖练习题(三层)

仅用于检测你是否良好掌握语法糖的顺序,实际中见到的非常少。

def outer1(func1):
    print('加载了outer1')
    def wrapper1(*args, **kwargs):
        print('执行了wrapper1')
        res1 = func1(*args, **kwargs)
        return res1
    return wrapper1

def outer2(func2):
    print('加载了outer1')
    def wrapper2(*args, **kwargs):
        print('执行了wrapper2')
        res2 = func2(*args, **kwargs)
        return res2
    return wrapper2

def outer3(func3):
    print('加载了outer3')
    def wrapper3(*args, **kwargs):
        print('执行了wrapper3')
        res3 = func3(*args, **kwargs)
        return res3
    return wrapper3

@outer1  # 名为index = outer1(wrapper2)
@outer2  # wrapper2 = outer2(wrapper3)
@outer3  # wrapper3 = outer3(真正的index)
def index():
    print('from index')

index()

9. 有参装饰器

# 简易版用户认证装饰器
from functools import wraps

def outer_auth(source_data):
	# source_data = 'file'.....等等
    def outer(func):
        @wraps(func)
        def inner(*args, **kwargs):
            # 获取用户名密码
            username = input('请输入用户名>>>:').strip()
            pwd = input('请输入密码>>>:').strip()
            # 校验用户名密码是否正确
            # 多种校验路径可选
            if source_data == 'file':
                print('file获取用户数据比对')
            elif source_data == 'MySQL':
                print('MySQL获取用户数据比对')
            else:
                print('用户名或密码错误,无法执行函数')

            if username == 'jason' and pwd == '123':
                res = func(*args, **kwargs)
                return res
        return inner
    return outer

@outer_auth('MySQL')
def index():
    print('from index')

index()