23、Python之无参装饰器

左心房为你撑大大i 提交于 2020-03-24 08:53:10

一、前置知识

1.1 *args, **kwargs

def index(x,y):
    print(x,y)

def wrapper(*args,**kwargs):
    index(*args,**kwargs) # 本质上相当于给wrapper传入什么,给index就是调用什么
    
wrapper(y=222,x=111)  # 111 222

1.2 名称空间与作用域

名称空间的的"嵌套"关系是在函数定义阶段,即检测语法的时候确定的

1.3 函数对象

# 1、可以把函数当做参数传入
# 2、可以把函数当做返回值返回

python中有while与for两种循环机制,其中while循环称之为条件循环,语法如下:

1.4 函数嵌套

函数的嵌套定义:
def outter(func):
	def wrapper():
		pass
	return wrapper
	

1.5 闭包函数

def outter():
    x=111
    def wrapper():
        x  
    return wrapper  # 不给wrapper传参的情况下,让参数可以在外层函数(包函数)取到值

f=outter()
# 传参的方式一:通过参数的形式为函数体传值

def wrapper(x):
    print(1)
    print(2)
    print(3)
    x

wrapper(1)
wrapper(2)
wrapper(3)

# 传参的方式二:通过闭包的方式为函数体传值
def outter(x):
    # x=1
    def wrapper():
        print(1)
        print(2)
        print(3)
        x
    return wrapper # return outter内的wrapper那个函数的内地址

f1=outter(1)
f2=outter(2)
f3=outter(3)

wrapper=outter(1)

二、什么是装饰器

# 1、器指的是工具,可以定义成函数
# 2、装饰指的是为其他事物添加添加额外的东西点缀	

三、为什么要用装饰器

开放封闭原则:
	开放:指的是对拓展功能是开放的。
	封闭:指的是对修改源代码是封闭的。
装饰器就是可以在不修改被装饰对象源代码以及调用方式的前提下,为被装饰对象添加额外功能的工具。有了装饰器,就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。

四、装饰器的实现

4.1 无参装饰器的实现

如果想为下述函数添加统计其执行时间的功能

def index(x,y,z):
    time.sleep(3)
    print('index %s %s' %(x,y,z))

index(111,222,333)
# index(111,y=222,z=333) # 不同的传参方式

Step1:将统计执行时间功能加上,不改变调用方式

import time

def index(x,y,z):
    start=time.time()
    time.sleep(3)
    print('index %s %s' %(x,y,z))
    stop = time.time()
    print(stop - start)

index(111,222,333)  # 调用方式没变,但是改变了源代码,不符合规范

Step2:遵循不修改被装饰对象源代码的原则,我们想到的解决方法可能是这样

import time

def index(x,y,z):
    time.sleep(3)
    print('index %s %s' %(x,y,z))

start=time.time()
index(111,222,333)
stop=time.time()
print(stop - start) 
#虽然加上了新功能,也没修改源代码及调用方式,但是代码冗余,每次都要复制此代码块。

Step3:由于上述代码冗余,所以采用了如下方案解决,但是带来了新的问题,函数的调用方式变了

import time

def index(x,y):
    time.sleep(3)
    print('index %s %s' %(x,y))

def wrapper():
    start=time.time()
    index(111,222)
    stop=time.time()
    print(stop - start)

wrapper()

Step3:优化一,将index参数参数写活,可以为index传入任意参数

import time

def index(x,y,z):
    time.sleep(3)
    print('index %s %s %s' %(x,y,z))

def wrapper(*args,**kwargs):  # 可以为index传入任意参数
    start=time.time()
    index(*args,**kwargs) # index(111,z=333,y=22)  可以为index传入任意参数
    stop=time.time()
    print(stop - start)

wrapper(111,222,333)
wrapper(111,z=333,y=222)

Step3:优化二,在优化一的基础上把被装饰对象写活了,原来只能装饰index

import time

def index(x,y,z):
    time.sleep(3)
    print('index %s %s %s' %(x,y,z))
print(index)  # <function index at 0x000001B7B1776280>

def home(name):
    time.sleep(2)
    print('welcome %s to home page' %name)


def outter(func):
    # func = index的内存地址  # wrapper内不能传入名字,所以使用闭包函数,使之在上层空间寻找到
    def wrapper(*args,**kwargs):
        start=time.time()
        func(*args,**kwargs) # index的内存地址()
        stop=time.time()
        print(stop - start)
    return wrapper  # 可以让wrapper在全局被调用到

index=outter(index) # 作用:使index=wrapper的内存地址
print(index)  # <function outter.<locals>.wrapper at 0x00000248D5922160>
home=outter(home) # home=wrapper的内存地址

index(111,y=222,z=333)  # index=wrapper的内存地址,传参后执行wrapper函数,增加了功能,不改变调用方式
# index 111 222 333
# 3.0008726119995117

home('egon')
# welcome egon to home page
# 2.000373125076294

Step3:优化三,将wrapper做的跟被装饰对象一模一样,以假乱真(使返回值一样)

import time

def index(x,y,z):
    time.sleep(3)
    print('index %s %s %s' %(x,y,z))

def home(name):
    time.sleep(2)
    print('welcome %s to home page' %name)

def outter(func):
    def wrapper(*args,**kwargs):
        start=time.time()
        res=func(*args,**kwargs)  # 赋值,变量函数的返回值
        stop=time.time()
        print(stop - start)
        return res  # 返回值,伪装成跟原函数一样

    return wrapper
# 偷梁换柱:home这个名字指向的wrapper函数的内存地址
home=outter(home)

res=home('egon') # res=wrapper('egon')
print('返回值--》',res)  # 返回值--》 None

# 大方向:如何在方案三的基础上不改变函数的调用方式

4.2 语法糖

# 被装饰对象正上方的单独一行写@装饰器名字,
# 用于解决每次使用装饰器都要运行一次函数名字与装饰器更换内存地址的操作

注意:装饰器须在语法糖调用上面

import time

装饰器
def timmer(func):
    def wrapper(*args,**kwargs):
        start=time.time()
        res=func(*args,**kwargs)
        stop=time.time()
        print(stop - start)
        return res

    return wrapper

在被装饰对象正上方的单独一行写@装饰器名字
@timmer # index=timmer(index)  不需要每次都写这个index
def index(x,y,z):
    time.sleep(3)
    print('index %s %s %s' %(x,y,z))

@timmer # home=timmer(ome)  
def home(name):
    time.sleep(2)
    print('welcome %s to home page' %name)

index(x=1,y=2,z=3)
home('egon')

4.3 无参装饰器模板

def outter(func):
    def wrapper(*args,**kwargs):
        # 1、调用原函数
        # 2、为其增加新功能
        res=func(*args,**kwargs)
        return res
    return wrapper
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!