10,python装饰器

1,装饰器的初识

装饰器:装饰,装修,房子本来就可以住,如果装修不影响住,而且体验更佳
器:工具
开放封闭原则:
'''
    开放:对代码的拓展是开放的,
    封闭:对源码的修改是封闭的。

'''

装饰器:完全遵循开放封闭原则。
'''
在不改变原函数的代码以及调用方式的前提下,为其增加新的功能。
'''
# 装饰器就是一个函数

装饰器的演变过程

例子:

给一段代码A添加一个功能,这个功能可以计算出代码A运行了多少时间。

版本一:

# 版本一:

import time


def index():
    '''有很多代码'''  # 模拟网络延迟
    time.sleep(3)  # 模拟网络延迟单位是3秒
    print('欢迎登录博客园首页!')


def dirty():
    '''有很多代码'''  # 模拟网络延迟
    time.sleep(3)  # 模拟网络延迟单位是3秒
    print('欢迎登录日记页面!')


start_time = time.time()
index()
end_time = time.time()
print(end_time - start_time)

start_time = time.time()
dirty()
end_time = time.time()
print(end_time - start_time)

就是只能在代码前后添加功能,复用性不强

版本二:

# 版本二

import time


def index():
    '''有很多代码'''  # 模拟网络延迟
    time.sleep(1)  # 模拟网络延迟单位是1秒
    print('欢迎登录博客园首页!')


def dirty():
    '''有很多代码'''  # 模拟网络延迟
    time.sleep(2)  # 模拟网络延迟单位是2秒
    print('欢迎登录日记页面!')


def timmer(f):  # 可以传参
    start_time = time.time()
    f()
    end_time = time.time()
    print(end_time - start_time)


timmer(index)  # 传进去哪个函数名就检测哪个函数
原来index()函数源码没有变化,给原函数添加了一个新的功能测试原函数的执行效率的功能
满足开放封闭原则吗?原函数的调用方式改变了

版本三:

不改变原函数的调用方式。
# 版本三

import time


def index():
    '''有很多代码'''  # 模拟网络延迟
    time.sleep(1)  # 模拟网络延迟单位是1秒
    print('欢迎登录博客园首页!')


def timmer(f):  # 可以传参
    def inner():
        start_time = time.time()
        f()
        end_time = time.time()
        print(f'函数的执行效率是{end_time - start_time}')
    return inner


index = timmer(index)     # index()
index()
如果有五百个函数则每此调用之前都要重新赋值 类似:index = timmer(index)

版本四:语法糖优化

# 版本四

def timmer(f):  # 可以传参
    start_time = time.time()
    f()
    end_time = time.time()
    print(f'函数的执行效率是{end_time - start_time}')


@timmer     # @timmer的意思就等同于index = timmer(index)
def index():
    '''有很多代码'''  # 模拟网络延迟
    time.sleep(1)  # 模拟网络延迟单位是1秒
    print('欢迎登录博客园首页!')

ret = index
版本四也有缺点:不能返回函数的返回值

版本五:

# 版本五

import time

# timmer()装饰器
def timmer(f):  # 可以传参
    def inner():
        start_time = time.time()
        r = f()
        end_time = time.time()
        print(f'函数的执行效率是{end_time - start_time}')
        return r
    return inner


@timmer  # @timmer的意思就等同于index = timmer(index)
def index():
    '''有很多代码'''  # 模拟网络延迟
    time.sleep(1)  # 模拟网络延迟单位是1秒
    print('欢迎登录博客园首页!')
    return 666
加上装饰器不应该改变原函数的返回值,所以666应该返回给下面的ret
但是下面的这个ret实际接收的是inner函数的返回值,而666返回给的是装饰器里面的
f()也就是r,我们现在要解决的问题就是将r给inner的返回值。

版本六:被装饰函数带参数

# 版本六

import time

# timmer()装饰器
def timmer(f):  # 可以传参
    def inner(n):
        start_time = time.time()
        r = f(n)
        end_time = time.time()
        print(f'函数的执行效率是{end_time - start_time}')
        return r
    return inner


@timmer  # @timmer的意思就等同于index = timmer(index)
def index(name):
    '''有很多代码'''  # 模拟网络延迟
    time.sleep(1)  # 模拟网络延迟单位是1秒
    print(f'欢迎{name}登录博客园首页!')
    return 666


index('小红')     # 相当于inner('小红')  因此inner()也要有参数,f()也要有参数
print(index('小红'))

版本七:多个参数的传递

# 版本七:多个参数的装饰器传参

import time


# timmer()装饰器
def timmer(f):  # 可以传参
    def inner(*args, **kwargs):
        start_time = time.time()
        r = f(*args, **kwargs)
        end_time = time.time()
        print(f'函数的执行效率是{end_time - start_time}')
        return r

    return inner


@timmer  # @timmer的意思就等同于index = timmer(index)
def index(name):
    '''有很多代码'''  # 模拟网络延迟
    time.sleep(0.5)  # 模拟网络延迟单位是1秒
    print(f'欢迎{name}登录博客园首页!')
    return 666


@timmer
def dirty(name, age):
    '''有很多代码'''  # 模拟网络延迟
    time.sleep(0.5)  # 模拟网络延迟单位是2秒
    print(f'欢迎{age}岁的{name}登录日记页面!')


@timmer
def student(name, age, high, count):
    print(f'{name}今年{age}岁,身高{high}学的有{count}专业。')


index('小红')
print(index('小红'))

dirty('alex', '18')

student('alex', '19', '185', ['音乐', '数学', '语文', '英语'])

装饰器模版

# 标准版的装饰器(模版)
def wapper(f):
    def inner(*args, **kwargs):
        '''
        添加新功能的代码区,被装饰函数执行前的操作
        '''
        ret = f(*args, **kwargs)  # 调用添加功能的源代码
        '''
        添加新功能的代码区,被装饰函数执行后的操作
        '''
        return ret

    return inner

标准装饰器的使用示例

**同级目录下擦护功能键一个passwd.txt的文件,内容如下

        大壮|123
        太白|666
        b哥|999
        alex|123
def login(f):
    def inner(*args, **kwargs):
        dic = {}
        with open('passwd.txt', encoding='utf-8') as file:
            for line in file:
                data = line.split("\n")
                a, b = data[0].split("|")
                dic[a] = b
        count = 0
        flag = True
        while flag:
            username = str(input('用户名:'))
            password = str(input('密 码:'))
            if username in dic.keys() and password == dic[username]:
                print('登录成功!')
                ret = f(username)
                return ret
            else:
                print(f'用户名或密码不正确,剩下{2 - count}次机会')
                count += 1
                if count == 3:
                    flag = False

    return inner


@login
def login_2(name):
    time.sleep(2)
    print(f'欢迎{name}登录博客园')


login_2()

2,带参数的装饰器

参数装饰器模版

# 模版

def ff(xxx):
    def func(f):
        def inner(*args, **kwargs):
            ret = f(*args, **kwargs)
            return ret

        return inner

    return func

需求:

将登录的日志信息写到auth.log文件中
所有的购物信息写到operate.log文件中
import time


def logger(path):
    def log(func):
        def inner(*args, **kwargs):
            ret = func(*args, **kwargs)
            with open(path, encoding='utf-8', mode='a') as f:
                msg = f"在{time.strftime('%Y-%m-%d %H:%M:%S')}执行了{func.__name__}\n\t"
                f.write(msg)
            return ret

        return inner

    return log


@logger('login.log')  # 装饰器参数,可以直接将日志写到auth.log文件中,也不需要修改装饰器,装饰器外层再嵌套一个函数
def login():
    print('登录的逻辑')


@logger('register.log')     # # 装饰器参数,可以直接将日志写到register.log文件中,也不需要修改装饰器
def register():
    print('注册的逻辑')


@logger('show_goods.log')  # 装饰器参数,可以直接将日志写到show_goods.log文件中,也不需要修改装饰器
def show_goods():
    print('查看所有商品信息')


@logger('add_goods.log')  # 装饰器参数,可以直接将日志写到add_goods.log文件中,也不需要修改装饰器
def add_goods():
    print('商品加入购物车')


login()
register()

3,装饰器的应用

装饰器的应用:登录认证

装饰器的应用场景很多,这里用登录认证来演示一下

模拟博客园的登录。装饰器的认证功能

装饰器的需求:

访问被装饰函数之前,写一个三次登录认证功能,
登录成功后:让其访问被装饰器的函数,登录没有成功,不能访问。

def login():
    print('请完成登录')


def register():
    print('请完成注册')


# 装饰器
'''
你的装饰器完成:访问被装饰函数之前,写一个三次登录认证功能,
登录成功后:让其访问被装饰器的函数,登录没有成功,不能访问。
'''

login_status_dic = {'username': None,
                    'status': False}


def auth(f):
    def inner(*args, **kwargs):
        '''访问函数之前的操作,3次认证登录,不成功不执行下一步'''
        if login_status_dic['status']:
            ret = f(*args, **kwargs)
            '''访问函数之后的操作'''
            return ret
        else:
            username = input('请输入用户名:').strip()
            password = input('请输入密码:').strip()
            if username == 'taibai' and password == '123':
                print('登录成功')
                login_status_dic['username'] = username
                login_status_dic['status'] = True
                ret = f(*args, **kwargs)
                return ret
            else:
                print('登录失败')

    return inner


@auth
def article():
    print('欢迎访问文章页面')


@auth
def comment():
    print('欢迎访问评论页面')


@auth
def dairy():
    print('欢迎访问日记页面')


article()
comment()
dairy()

4,函数的总结

# 仅限关键字参数
def func(a, b, *args, c):
    print(a, b)
    print(c)
    print(args)


func(1, 2, 3, 4, 8, 5, 6, c=666)


# *
a, b, *c = (1, 2, 3, 4, 5, 6, 7, 8)
print(a, b, c)
a, *b, c = [11, 22, 33, 44, 55, 66]
print(a, b, c)
a, *b, c = range(10)
print(a, b, c)


# 全局名称空间:global
def func():
    global name
    name = 'alex'

func()
print(name)


name = '太白'
def func():
    global name
    name = 'alex'

print(name)
func()
print(name)


nonlocal
def func():
    name = '太白'
    def inner():
        nonlocal name
        name = 'alex'
    print(name)
    inner()
    print(name)

func()


# 函数名
def func():
    pass


print(func)  # <function func at 0x000002A6F7136798>


# 获取自由变量
def wrapper(a):
    name = '太白'
    def inner():
        print(a)
        print(name)
    return inner
ret = wrapper('烧饼')
print(ret())
print(ret.__code__.co_freevars)


# 装饰器
def func(f):
    def inner(*args, **kwargs):
        ret = f(*args, **kwargs)
        return ret

    return inner

更多内容可以查看自己学python的过程,简单笔记。-CSDN博客

更多内容可以查看自己学python的过程,简单笔记。-CSDN博客

更多内容可以查看自己学python的过程,简单笔记。-CSDN博客

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值