Fork me on GitHub

装饰器

可变长参数

定义

可变长指的是参数的个数不固定
站在实参的角度,实参是用来为形参赋值的,如果实参的个数不固定那么必须要有对应的形参能够接收溢出的实参

1在形参名前加*

*会把溢出的位置的实参存成元组,然后赋值给形参名

def func(x,*y):
    print(x)
    print(y)
func(1,222,333,4555)
func(1)   #返回的则是空元组
func()    #位置形参x必须被传值,不然报错

#示范:
def add(*nums):   #将传入的字符聚拢成元组
    res=0
    for num in nums:
        res+=num
    return res
print(add(111,222,33344,111))

2.在形参名前加**

**会把溢出的关键字实参存成字典,
然后赋值其后的形参名

def func(x,**y):
    print(x)
    print(y)
func(111,a=22,b=111,c=444) #位置实参要在关键字实参的前面
**返回的是字典
func(a=22,b=111,111,c=444) #报错

3.在实参前加*

*会把输入的值打散成位置实参

def func(x, y, z):
    print(x, y, z)
nums=[1,2,3]
nums = (1, 2, 3)
func(*nums)  # func(1,2,3)

4.在实参前加**

**会把其后的值打散成关键字实参
dic={'y':2,'z':3,'x':1}
func(**dic)  #func(y=2,z=3,x=1)


def index(x,y,z,a,b,c):
    print('====>index',x,y,z,a,b,c,)


def wrapper(*args,**kwargs): #聚拢
    index(*args,**kwargs)    #打散,并通过位置与关键字参数一一赋值

wrapper(1,2,3,a=111,b=222,c=333) #此时的传参其实就是给index传参


def index(x,y,z,a,b,c):
    print('=====>index',x,y,z,a,b,c,)

def wrapper(*args,**kwargs):
    index(*args,**kwargs)

wrapper(111,111,111,a=222,b=222,c=222)

def login():
    print('登录功能....')

def register():
    print('注册功能....')

def recharge():
    print('充值功能....')

def withdraw():
    print('提现功能....')

def transfer():
    print('转账功能....')

func_dic={'0':['退出',exit],
          '1':['登录',login],
          '2':['充值',recharge],
          '3':['提现',withdraw],
          '4':['转账',transfer]
          }
def main():
    while True:
        print('有账号直接登录----->请输入0\n'
              '没账号请先注册----->请输入1')
        cmd=input('请输入指令:')
        if cmd=='0':
            for k in func_dic:
                print(k,func_dic[k][0])
            cmd1=input('请输入命令编号:')
            func_dic[cmd1][1]()

        elif cmd=='1':
            register()

        else:
            print('输入错误,重新输入')

了解

def func(x,y=222,*args,n=777,m,**kwargs): #m夹在可变长参数中间必须用关键实参取值,所以可跟在默认形参后面
    print(x)
    print(y)
    print(args)
    print('m====>',m)
    print('n====>',n)
    print(kwargs)
func(111,2,3,1,2,1,2,m=22222,a=333,b=3333)

无参装饰器

1.什么是装饰器

器:工具
装饰:为被装饰器添加额外的功能

2.为何要有装饰器

软件一旦上线运行之后,就应该遵循开放封闭原则:
    1.开放指的是对拓展新功能开放
    2.封闭指的是对修改源代码封闭
定义装饰器的目的:
    定义装饰器就是为了在遵循1和2的前提下来为其他函数添加新功能的

PS:
不修改被装饰对象指的是定义与调动都不能修改
所以下述行为都违反了开放封闭原则:
    1.修改被装饰对象定义时的源代码
    2.修改被装饰对象的调用方式

3.如何用装饰器

一、被装饰器对象index如下

import time
def index(x,y):
    time.sleep(1)
    print('index=====>',x,y)
index(1,2)

二、为index添加新功能
2.1方案一:直接修改源代码为被装饰对象index添加新功能
问题:修改了源代码

import time
def index(x,y):
    start_time = time.time()
    time.sleep(1)
    print('index===>',x,y)
    stop_time = time.time()
    print('run time is: %s' %(stop_time - start_time))

index(1,2)

2.2方案二:找到所有调用index的位置,然后添加代码
问题:需要写好多段重复的代码

import time
def index(x,y):
    time.sleep(1)
    print('index=====>',x,y)
start_time = time.time()
index(1,2)
stop_time = time.time()
print('run time is: %s' %(stop_time - start_time))

2.3方案三:把装饰功能写到一个函数内
问题:装饰功能被写死了,只能用来装饰index函数了

import time
def index(x,y):
    time.sleep(1)
    print('index=====>',x,y)
def wrapper():
    start_time = time.time()
    index(1, 2)
    stop_time = time.time()
    print('run time is: %s' % (stop_time - start_time))
wrapper()

2.4方案四:把wrapper函数内的index修改为参数的形式
问题:直接为wrapper函数传参的方案不行,因为原函数的调用
是在全局调用index(1,2),而现在的调用统一都变成了wrapper(index),修改装饰器的调用方式

def index(x,y):
    time.sleep(1)
    print('index=====>',x,y)

def wrapper(func):
    start_time = time.time()
    func(1, 2)
    stop_time = time.time()
    print('run time is: %s' % (stop_time - start_time))
wrapper(index)

2.5方案五:基于闭包函数把wrapper函数想要的参数包给它,然后基于函数对象把闭包函数wrapper返回到全局,赋值给原函数名
问题:把index伪装成了wrapper,但伪装的不够像:参数的传递与原函数不一致

def index(x,y):
    time.sleep(1)
    print('index=====>',x,y)
def outter(func):
    def wrapper():
        start_time = time.time()
        func(1, 2)
        stop_time = time.time()
        print('run time is: %s' % (stop_time - start_time))
    return wrapper
index=outter(index)
index() #参数被确定

2.6方案六:在方案五的基础上基于*args,**kwargs,把wrapper的参数伪装成跟被装饰函数一模一样
问题:返回值不一样

def index(x,y):
    time.sleep(1)
    print('index=====>',x,y)

def outter(func):
    def wrapper(*args,**kwargs):
        start_time = time.time()
        func(*args, **kwargs)
        stop_time = time.time()
        print('run time is: %s' % (stop_time - start_time))
    return
index=outter(index)
index()

2.7方案七:在方案六的基础上把wrapper函数的返回值与被装饰函数保持一致
问题:装饰器使用的不够简洁

import time
def outter(func):
    def wrapper(*args,**kwargs):
        s_time=time.time()
        res=func(*args,**kwargs)
        print(f'时间已过{time.time()-s_time}')
        return res   #返回原函数的返回值
    return wrapper

def index(x,y):
    time.sleep(1)
    print(x,y,'======>index')
    return 10      #执行了此代码
index=outter(index)
index(1,2)         #执行了return只是没打印,那么装饰器中的wrapper也是同理
import time
def outter(func):
    def wrapper(*args,**kwargs):
        s_time=time.time()
        res=func(*args,**kwargs)
        print(f'时间已过{time.time()-s_time}')
        return res   #返回原函数的返回值
    return wrapper


def index(x,y):
    time.sleep(1)
    print(x,y,'======>index')
         #执行了此代码

index=outter(index)
print(index(1,2))   #此时index里面没有return,那么装饰器中的wrapper中的return返回值为空,最后打印出来即是None

2.8方案八:在方案7的基础上使用装饰器的语法糖@符号让装饰器的使用变得简洁
import time

def timmer(func):
    def wrapper(*args, **kwargs):  # func给wrapper的形参传入实参
        start_time = time.time()
        res = func(*args, **kwargs)
        stop_time = time.time()
        print('run time is: %s' % (stop_time - start_time))
        return res

    return wrapper


@timmer  # home=timmer(home)
def home(name):
    "home的文档注释"
    time.sleep(0.5)
    print('welcome %s to home page' % name)
    return 123


res = home('egon')

有参装饰器

在原来无参装饰器的基础上在包一层函数,将参数一层一层的传入进去

import time

def outter(engine):
    def arth(func):
        def wrapper(*args,**kwargs):
            inp_name=input('请输入你的账户:')
            inp_pwd=input('请输入你的密码:')
            if engine=='file':
                if inp_name=='arther' and inp_pwd=='123':
                    print('登录成功')
                    res=func(*args,**kwargs)
                    return res
                else:
                    print('登录失败')
            elif engine=='mysql':
                print('数据来自mysql')
            elif engine=='ldap':
                print('数据来自mysql')
            else:
                print('数据来自其他engine')
        return wrapper
    return arth
@outter('file')
def index(x,y):
    print('index===>',x,y)
    time.sleep(1)
    return 'index内的函数'
res=index(111,222)
print(res)

有参装饰器的模板:

def outter2(x,y,z,a,b):
    def outter1(func):
        def wrapper(*args,**kwargs):
            res=func(*args,**kwargs)
            return res
        return wrapper
    return outter1

叠加多个装饰器

1、无参装饰器的模板

def outter(func):
def wrapper(args,**kwargs):
res=func(
args,**kwargs)
return res
return wrapper

2、叠加多个装饰器

2.1 加载顺序:自下而上
2.2 执行顺序:自上而下运行内层的wrapper函数

def deco1(func1):  # func1 = wrapper2的内存地址
    def wrapper1(*args,**kwargs):
        print('wrapper1====>')
        res1=func1(*args,**kwargs)
        return res1
    return wrapper1

def deco2(func2):  # func2 = wrapper3的内存地址
    def wrapper2(*args,**kwargs):
        print('wrapper2====>')
        res2=func2(*args,**kwargs)
        return res2
    return wrapper2

def deco3(func3):  # func3 = 最原始的那个被装饰函数的内存地址
    def wrapper3(*args,**kwargs):
        print('wrapper3====>')
        res3=func3(*args,**kwargs)
        return res3
    return wrapper3

        # index=wrapper1的内存地址                              index=wrapper1 被先执行 所以被赋予相同的函数名
@deco1  # deco1(wrapper2的内存地址)=>wrapper1的内存地址           wrapper2=deco1(wrapper2)=wrapper1
@deco2  # deco2(wrapper3的内存地址)=>wrapper2的内存地址           wrapper3=deco2(wrapper3)=wrapper2
@deco3  # deco3(最原始的那个被装饰函数的内存地址)=>wrapper3的内存地址 index=deco3(index)=wrapper3
def index(x,y):
    print('index=>',x,y)

index(1,2)

从下到上加载,然后全局调用的原函数名其实是最外层的装饰器伪装的,从上到下执行,遇到了内装饰器的内存地址就去执行内装饰器,一直运行到原函数执行完然后返回值,
然后再按原轨迹自下而上的依次调回到原来的执行点继续执行并return(这是一个自然结束的过程,arth包含着index,index结束了那么跟着结束的就是arth)

案例

import time

def timmer(func):  # 被丢了出去在全局调用
    def wrapper1(*args, **kwargs):
        s_time = time.time()
        res = func(*args, **kwargs)  # 相当于wapper2的内存地址
        print(f'时间已过{time.time() - s_time}')
        return res  # 返回原函数的返回值

    return wrapper1

def arth(func):
    def wrapper2(*args, **kwargs):
        inp_name = input('请输入你的账户名:')
        inp_pwd = input('请输入你的密码:')
        if inp_name == 'arther' and inp_pwd == '123':
            print('登录成功')
            res = func(*args, **kwargs)  # 原始index的内存地址
            return res  # 返回原函数的返回值
        else:
            print('登录失败')

    return wrapper2

@timmer
@arth
def index(x, y):
    time.sleep(1)
    print(x, y, '======>index')
    return 123  # 执行了此代码


index(1, 2)  # 相当于调用最外层装饰器

所以最后打印出来的时间段包含了执行原函数index以及第一层装饰器arth的时间

posted @ 2020-09-01 10:33  artherwan  阅读(96)  评论(0编辑  收藏  举报