装饰器、开放封闭原则、语法糖、万能装饰器、带参数的装饰器、 多个装饰器装饰一个函数

装饰器

装饰器作用:在不修改函数的调用方式,但是还想在原来的函数前后添加功能

案例:测试200个函数,每个函数的运行时间是多少

# 建议进Debug模式查看运行
#需求获取一段代码得运行时间,架设需要测200个函数代码
import time  # 导入时间模块
def func():
    #start = time.time()  # 获取当前时间
    time.sleep(0.3)  # 当程序执行到这里得时候,在多少秒后接着执行
    print('hahaha')
    #end = time.time()
    #print(end-start) # 获取时间差

def timmer(f):  # f获得了func的内存地址 f = func   # 装饰器函数
    def inner():
        start = time.time()  # 获取当前时间
        f() # 调用func()  # 被装饰的函数
        end = time.time()
        print(end-start) # 获取时间差
    return inner
func = timmer(func)  # timmer函数里的实参是需要测试的函数名 # func获得timmer函数的返回值 inner
func() # 运行inner()

# 不想修改函数的调用方式,但是还想在原来的函数前后添加功能
# timmer就是一个装饰器函数,只是对一个函数有一些装饰作用

 开放封闭原则

  开放:对扩张是开放的,即在已有的功能环境下,新增更多的功能

# 这段代码是装饰器函数,在不修改原代码的情况下,新增功能
def timmer(f):  # f获得了func的内存地址 f = func
    def inner():
        start = time.time()  # 获取当前时间
        f() # 调用func()
        end = time.time()
        print(end-start) # 获取时间差
    return inner

  封闭:对修改是封闭的,即已经封板的程序不能再修改。

# 这段函数相当于封板的,不能再修改了,比如修改sleep会改变我整个函数
def func():
    #start = time.time()  # 获取当前时间
    time.sleep(0.3)  # 当程序执行到这里得时候,在多少秒后接着执行
    print('hahaha')
    #end = time.time()
    #print(end-start) # 获取时间差

 语法糖 (@)

@装饰器的函数名,同时该语法糖紧接着下行就是被装饰的函数。

import time  # 导入时间模块

def timmer(f):  # f获得了func的内存地址 f = func
    def inner():
        start = time.time()  # 获取当前时间
        ret = f() # 调用func() 获取func()函数的返回值
        end = time.time()
        print(end-start) # 获取时间差
        return ret # 获得上面ret的值
    return inner

@timmer # 等同于func=timmer(func)
def func():
    #start = time.time()  # 获取当前时间
    time.sleep(0.3)  # 当程序执行到这里得时候,在多少秒后接着执行
    print('hahaha')
    #end = time.time()
    #print(end-start) # 获取时间差

# func = timmer(func)  # timmer函数里的实参是需要测试的函数名 # a获得timmer函数的返回值 inner
ret = func()

 进阶,装饰带函数的装饰器

import time  # 导入时间模块

def timmer(f):  # f获得了func的内存地址 f = func
    def inner(*args,**kwargs): # 动态参数,可以接受任意参数
        start = time.time()  # 获取当前时间
        ret = f(*args,**kwargs) # 调用func() # 动态参数,可以接受任意参数
        end = time.time()
        print(end-start) # 获取时间差
        return ret
    return inner

@timmer # 等同于func=timmer(func)
def func(a,b): # 传参
    #start = time.time()  # 获取当前时间
    time.sleep(0.3)  # 当程序执行到这里得时候,在多少秒后接着执行
    print('hahaha',a,b)

    #end = time.time()
    #print(end-start) # 获取时间差

# func = timmer(func)  # timmer函数里的实参是需要测试的函数名 # a获得timmer函数的返回值 inner
ret = func(1,2) # inner() #传参

万能装饰器---装饰器的固定写法

#装饰器的固定写法

def wrapper(f):  # 装饰器含函数,f是被装饰的函数
    def inner(*args,**kwargs):
        '''在被装饰函数之前要做的事'''
        ret = f(*args,**kwargs)  # 被装饰的函数 
        '''在被装饰函数之后-要做的事'''
        return ret
    return inner

@wrapper # test = wrapper(test)
def test():
  print(234)

ret = test()

进阶,显示函数名

from functools import wraps  # 导入已经写好的装饰器  作用是使用__name__打印函数名的时候 直接显示对应的函数名
def wrapper(f):  # 装饰器含函数,f是被装饰的函数
    @wraps(f)  # 给函数inner进行装饰 
    def inner(*args,**kwargs):
        '''在被装饰函数之前要做的事'''
        ret = f(*args,**kwargs)  # 被装饰的函数
        '''在被装饰函数之后-要做的事'''
        return ret
    return inner

@wrapper
def holiday(day):
    print('全体放假%s天'%day)
    return '好开心'

ret = holiday(3)
print(ret)
print(holiday.__name__) # 测试 不使用@wraps()语法糖

 使用示例

#编写装饰器,为多个函数加上认证的功能(用户名和密码来自文件)
#要求登录成功一次,后学的函数无需再输入用户名和密码
def userpw(): # 定义函数打开文件
    with open('up', encoding='utf-8') as f:
        l = []
        for i in f:
            l.append(i.strip())
    return l

t = False
def login(func):  # 装饰器
    def inner(*args, **kwargs):
        l = userpw()  # 调用函数打开文件
        global t  # 申明全局变量
        if t:  # 等同于t = True
            ret = func(*args, **kwargs)  # 被装饰的函数
            return ret
        else:
            username = input('username:')
            if username == l[0]:
                password = input('password:')
                if password == l[1]:
                    t = True  # 改变全局变量
                    ret = func(*args, **kwargs)  # 被装饰的函数
                    return ret
                else:
                    print("password error")
            else:
                print("username error")

    return inner

@login
def test():
    print('hello')

@login
def test1():
    print('hello1')

test()

test1()

 带参数的装饰器

技巧:先写一个装饰器,然后在装饰外层再嵌套一个函数,返回装饰器的函数名,然后再通过语法糖调用该函数即可:@外层函数(形参)

#假如你有300个参数,计算运行时间,你需要统一控制该装饰器是否生效

#初始版本
import time
def timmer(func):
    def inner(*args,**kwargs):
        start = time.time()
        ret = func(*args,**kwargs)
        end = time.time()
        print(end - start)
        return ret

    return inner

@timmer
def test():
    time.sleep(0.1)
@timmer
def test1():
    time.sleep(0.1)

test()
test1()

#进阶版本,带参数
import time
FLAG = True  # 定义全局变量
def timmer_out(flag):
    def timmer(func):
        def inner(*args,**kwargs):
            if flag:  # 判断全局变量是否为True
                start = time.time()
                ret = func(*args,**kwargs)
                end = time.time()
                print(end - start)
                return ret
            else:
                ret = func(*args,**kwargs)
                return ret
        return inner
    return timmer


#@timmer_out(FLAG) 等用于下面两句的组合
#timmer = timmer_out(FLAG)
#@timmer 
@timmer_out(FLAG) # 因为加了括号 所以先调用timmer_out()函数,该函数运行后返回timmer,即@timmer了 def test(): time.sleep(0.1) @timmer_out(FLAG) def test1(): time.sleep(0.1) test() test1()
FLAG=True # 改变全局参数
test()
test1()

 多个装饰器装饰一个函数

注意:语法糖的运行顺序,可以通过Debug模式进行观察

技巧:类似俄罗斯套娃,先执行第一个语法糖的第一段文字,接着执行第二个语法糖的第一段文字,接着执行被装饰的函数,再接着执行第二个语法糖的最后一句,最后执行第一个语法糖的最后一句

ef wrapper1(func): # func --> f
    def inner1():
        print('这是第一个函数的前一句')
        ret = func() # f()
        print('这是第一个函数的后一句')
        return ret
    return inner1

def wrapper2(func): # func --> inner1
    def inner2():
        print('这是第二个函数的前一句')
        ret = func()  # inner1()
        print('这是第二个函数的后一句')
        return ret
    return inner2
#
# # 根据语法糖的规则,@wrapper2 下层没有函数,无法执行,@wrapper1下层有函数,就优先执行@wrapper1
@wrapper2  # f = wrapper2(f) =wrpper2(inner)== inner2 因为已经执行了wrapper1 f已经赋值返回inner1
@wrapper1 # f = wrapper1(f) == inner1
def f():
    print('这是被装饰的函数')
    return "哈哈哈"

ret = f()  # inner2()
print(ret)

#常用地方
#记录用户的登陆情况
#计算这个函数的运行时间

 

posted on 2019-02-12 16:11  Jerry-Wang  阅读(153)  评论(0)    收藏  举报