12..装饰器

Posted pythonblogs

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了12..装饰器相关的知识,希望对你有一定的参考价值。

def wrapper(f):
    def inner(*args,**kwargs):
        '被装饰之前的函数'

1.开发封闭原则

软件面世时,不可能把所有的功能都设计好,当前的未来一两年功能给你上线,定期更新迭代。对于软件之前的写的源代码一般都不会修改,对函数里面的代码以及函数的调用方式。

开放原则:在源码不改变的情况下,增加一些额外的功能。

封闭原则:不要改变源码

python中装饰器:完美的诠释了的开放封闭原则。

装饰器就是一个函数: 他要装饰一个函数,在不改变原函数的源码以及调用方式的前提下,给其增加一个额外的功能。

27.1 初识装饰器

1 李业,在一家xx科技有限公司工作,主管安排了一个任务,写一个代码测试怼怼哥写的函数的执行效率

import time
def index():
    time.sleep(2)
    print('欢迎访问博客园首页')


print(time.time())
start_time = time.time()
index()
end_time = time.time()
print(f'此函数的执行效率end_time-start_time')

2. 主管让你测试小邓,李大象,重复代码太多。

def func1():
    time.sleep(2)
    print('欢迎访问日记首页')


def func2():
    time.sleep(1)
    print('欢迎访问评论首页')

start_time = time.time()
func1()
end_time = time.time()
print(f'此函数的执行效率end_time-start_time')

start_time = time.time()
func2()
end_time = time.time()
print(f'此函数的执行效率end_time-start_time')

3.  整合到函数中

def func1():
    time.sleep(2)
    print('欢迎访问日记首页')


def func2():
    time.sleep(1)
    print('欢迎访问评论首页')


def test_time(x):
    start_time = time.time()
    x()
    end_time = time.time()
    print(f'此函数的执行效率end_time-start_time')

test_time(func1)
test_time(func2)


4. 怼怼哥这个函数在实际项目中被500执行,主管要求:在被执行此函数时,
同时要测试一下被执行函数的效率。


def index():
    time.sleep(2)
    print('欢迎访问博客园首页')

# index()
def test_time(x):
    start_time = time.time()
    x()
    end_time = time.time()
    print(f'此函数的执行效率end_time-start_time')

test_time(index)

版本4的问题: 开放原则满足了,封闭原则:不改变原函数的源码,以及调用方式。
违反了封闭原则:改变了函数的调用方式。


版本5: 不能改变原函数的调用方式(闭包):


def index():
    time.sleep(2)
    print('欢迎访问博客园首页')

index()

def func1():
    time.sleep(2)
    print('欢迎访问日记首页')

def test_time(x):  # x = index
    def inner():
        start_time = time.time()
        x()
        end_time = time.time()
        print(f'此函数的执行效率end_time-start_time')
    return inner

index = test_time(index)
index()

语法糖@加上装饰器函数的名

def test_time(x):  # x = index
    def inner():
        start_time = time.time()
        x()
        end_time = time.time()
        print(f'此函数的执行效率end_time-start_time')
    return inner


# @test_time  # index = test_time(index)
def index():
    time.sleep(2)
    print('欢迎访问博客园首页')

index = test_time(index)
index()

def func1():
    time.sleep(2)
    print('欢迎访问日记首页')

@test_time
def func2():
    time.sleep(1)
    print('欢迎访问评论首页')

func2 = test_time(func2)
func3 = test_time(func3)
func2()
技术图片
技术图片
z
27.2 被装饰函数带返回值
def test_time(x):  # x = index
    def inner():
        start_time = time.time()
        ret = x()
        # print(F'ret: ret')
        end_time = time.time()
        print(f'此函数的执行效率end_time-start_time')
        return ret
    return inner


@test_time  # index = test_time(index)
def index():
    time.sleep(0.5)
    print('欢迎访问博客园首页')
    return True

print(index())  # inner()
27.3 被装饰函数带参数
无论加不加装饰器,你的实参'太白金星'应该传给形参n,。
但版本6不能实现传参,index('太白金星') ==  inner('太白金星')
import time
struct_time = time.localtime()
# print(time.strftime("%Y-%m-%d %H:%M:%S",struct_time))

def test_time(x):# x = index
    def inner(*args,**kwargs):
        #函数的定义:* **聚合
        # args = ('苹果')
        # args =(1,3)
        start_time = time.time()
        ret = x(*args,**kwargs)
        #函数的执行:* **打散
        # ret = x(*('苹果')) ==x('苹果',)
        # ret = x(*(1,3)) == x(1,3)
        #print(f'ret:ret')
        end_time = time.time()
        print(f'此函数的效率end_time-start_time')
        return ret
    return inner
@test_time  # index = test_time(index)
def index(n):
    time.sleep(0.5)
    print(f'欢迎n访问博客园首页')
    return True
@test_time  # index = test_time(index)
def func2(a,b):
    time.sleep(0.5)
    print(f'最终结果:a+b')
    return a + b


print(index('苹果'))  # inner('苹果')
print(func2(1,3)) # == inner(1,3)
27.4 标准版装饰器
def warpper(f):
    def inner(*args,**kwargs):
        '''被装饰函数之前的操作'''
        # print(657)
        ret = f(*args,**kwargs)
        '''被装饰函数之后的操作'''
        # print('执行完毕了')
        return ret
    return inner

@warpper
def func():
    print(111)
func()

装饰器的应用:在不改变原函数的源码以及调用方式前提下,为其增加额外的功能。登陆认证,打印日志等。

def wrapper(f):
    def inner(*args,**kwargs):
        ret = f(*args,**kwargs)
        return ret
    return inner
27.5 模拟博客园登录
dic_status = 
    'username':None,
    'status':False

#方法一:
# def get_pwd():
#   dic = 
#   with open('qq',encoding='utf-8')as f1:
#       for i in f1:
#           line = i.strip().split("|")
#           dic[line[0]] = line[1]
#       return dic
# 方法二:
def get_pwd():
    dic = 
    with open('qq',encoding='utf-8')as f1:
        for i in f1:
            user,pwd = i.strip().split("|")
            dic[user] = pwd
        return dic
# 方法三:
# def get_pwd():
#   with open('qq', encoding='utf-8')as f1:
#       return i.strip().split('|')[0]:i.strip().split('|')[1] for i in f1#字典推导式
def login(dic_pwd):
    dic1 = dic_pwd()
    count = 0
    while count < 3:
        username = input('请输入用户名').strip()
        password = input('请输入密码').strip()
        # if username in dic1 and password == dic1[username]:
        if dic1.get(username) == password:
            print('登录成功')
            dic_status['username'] = username
            dic_status['status'] = True
            return True
        else:
            print('用户名或密码错误')
        count += 1
    return False
def auth(f):
    def inner(*args,**kwargs):
        '''要进行登陆认证:
            有两个分支:
                1,如果之前登陆成功了,直接访问。
                2,如果这是之前没有登陆过,先进行三次登陆。
        '''
        if dic_status['status']:
            ret = f(*args,**kwargs)
            return ret
        else:
            if login(get_pwd):
                ret = f(*args, **kwargs)
                return ret
    return inner
@auth
def article():
    print('欢迎访问文章页面')


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


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


article()
diary()
comment()
27.6 装饰器的执行步骤
def wrapper(f):  # 1,执行wrapper函数,并且将func函数名 传给 变量f
    def inner(*args,**kwargs):
        print(111)  # 5,打印111
        ret = f(*args,**kwargs)  # 6,执行真正的func函数
        print(222)  # 8, 打印222
        return ret
    return inner

@wrapper  # func = wrapper(func)  # 2,将inner函数名给了重新定义的变量 func = inner
def func():
    print(333)  # 7,打印333

print(444)  # 3, 打印 444
func()  # 4,inner() 执行inner函数
print(555)  # 9,打印555

def func():
    print(666)

func = inner
print(func)
27.7 带参数的装饰器
def wrapper(f):
    def inner(*args,**kwargs):
        if f.__name__ == 'qq': #通过函数名

            ret = f(*args,**kwargs)
            return ret
        else:
            pass

    return inner

def wrapper_out(n,*args,sex='男',):
    def wrapper(f):  # f
        def inner(*args,**kwargs):
            ret = f(*args,**kwargs)  # func1()
            return ret
        return inner
    return wrapper
def func1():
    print('in func1')
func = wrapper_out(1)  # wrapper函数名
ly = func(func1)  # wrapper(func1) = inner
ly()  # inner()

从两个文件读取账号密码,登录

方法一:代码多,重复

def wrapper_out(n):
    def wrapper(f):
        def inner(*args,**kwargs):
            if n == 'qq':
                username = input('请输入用户名:').strip()
                password = input('请输入密码:').strip()
                with open('qq',encoding='utf-8') as f1:
                    for line in f1:
                        user,pwd = line.strip().split('|')
                        if username == user and password == pwd:
                            print('登陆成功')
                            ret = f(*args,**kwargs)
                            return ret
                    return False
            elif n == 'tiktok':
                username = input('请输入用户名:').strip()
                password = input('请输入密码:').strip()
                with open('tiktok', encoding='utf-8') as f1:
                    for line in f1:
                        user, pwd = line.strip().split('|')
                        if username == user and password == pwd:
                            print('登陆成功')
                            ret = f(*args, **kwargs)
                            return ret
                    return False    
      
        return inner
    return wrapper
@wrapper_out('qq')
def qq():
    print('成功访问qq')


@wrapper_out('tiktok')
def tiktok():
    print('成功访问抖音')

qq()
tiktok()

方法二:直接判断n 传进来的路径。

def wrapper_out(n):
    def wrapper(f):
        def inner(*args,**kwargs):
          username = input('请输入用户名:').strip()
          password = input('请输入密码:').strip()
            with open(n,encoding='utf-8') as f1:
                for line in f1:
                    user,pwd = line.strip().split('|')
                    if username == user and password == pwd:
                        print('登陆成功')
                        ret = f(*args,**kwargs)
                        return ret
                return False
        return inner
    return wrapper
@wrapper_out('qq')
def qq():
    print('成功访问qq')


@wrapper_out('tiktok')
def tiktok():
    print('成功访问抖音')

qq()
tiktok()
@wrapper_out('qq')
def qq():
    print('成功访问qq')
qq()
看到带参数的装饰器分两步执行:
'''
@wrapper_out('腾讯')
    1. 执行wrapper_out('腾讯') 这个函数,把相应的参数'腾讯' 传给 n,并且得到返回值 wrapper函数名。
    2. 将@与wrapper结合,得到我们之前熟悉的标准版的装饰器按照装饰器的执行流程执行。
'''
"""

方法三:qq 抖音两个文件取账号密码,带参数的装饰器

status = 'flag':False,'username':None
def get_pwd(path):
    dic = 
    with open(path, encoding='utf-8')as f1:
        for i in f1:
            line = i.strip().split('|')
            dic[line[0]] = line[1]
        return dic
def login(dic_pwd):
    dic1 = dic_pwd
    username = input('请输入用户名:')
    pwd = input('请输入密码:')
    if  username in dic1 and dic1[username]==pwd:
        print('登录成功')
        status['flag'] = True
        status['username'] = username
        return True
    else:
        return False
def wrapper_out(n):
    def wrapper(f):
        def inner(*args,**kwargs):
            if login(get_pwd(n)):
                ret = f(*args,**kwargs)
                return ret
        return inner
    return wrapper
@wrapper_out('qq')
def qq():
    print('成功访问qq')

@wrapper_out('douyin')
def ti():
    print('成功访问抖音')
qq()
ti()
27.8 多个装饰器装饰一个函数
def wrapper1(func1):  # func1 = f原函数
    def inner1():
        print('wrapper1 ,before func')  # 2
        func1()
        print('wrapper1 ,after func')  # 4
    return inner1

def wrapper2(func2):  # func2 == inner1
    def inner2():
        print('wrapper2 ,before func')  # 1
        func2()  # inner1
        print('wrapper2 ,after func')  # 5
    return inner2


@wrapper2  # f = wrapper2(f) 里面的f == inner1  外面的f == inner2
@wrapper1  # f = wrapper1(f) 里面的f == func1  外面的 f == inner1
def f():
    print('in f')  # 3

f()  # inner2()

技术图片

技术图片

def wrapper1(func):
    def inner1():
        print('wrapper1 ,before func')
        ret = func()
        print('wrapper1 ,after func')
        return ret
    return inner1

def wrapper2(func):
    def inner2():
        print('wrapper2 ,before func')
        ret = func()
        print('wrapper2 ,after func')
        return ret
    return inner2

def wrapper3(func):
    def inner3():
        print('wrapper3 ,before func')
        ret = func()
        print('wrapper3 ,after func')
        return ret
    return inner3

@wrapper3
@wrapper2
@wrapper1
def f():
    print('in f')
    return '哈哈哈'

print(f())

以上是关于12..装饰器的主要内容,如果未能解决你的问题,请参考以下文章

python 基础篇 12 装饰器进阶

python全栈闯关--12-装饰器进阶

18 12 14 python提高 装饰器

Python基础12-装饰器

CQRS 命令/查询装饰器

python 类装饰器