装饰器函数

装饰器函数

开发封闭原则(先从别人偷来一波好文章,简单易懂)

  什么是开放封闭原则?有的同窗问开放,封闭这是两个反义词这还能组成一个原则么?这不先后矛盾么?其实不矛盾。开放封闭原则是分状况讨论的。html

  咱们的软件一旦上线以后(好比你的软件主要是多个函数组成的),那么这个软件对功能的扩展应该是开放的,好比你的游戏一直在迭代更新,推出新的玩法,新功能。可是对于源代码的修改是封闭的。你就拿函数举例,若是你的游戏源代码中有一个函数是闪躲的功能,那么你这个函数确定是被多个地方调用的,好比对方扔雷,对方开枪,对方用刀,你都会调用你的闪躲功能,那么若是你的闪躲功能源码改变了,或者调用方式改变了,当对方发起相应的动做,你在调用你的闪躲功能,就会发生问题。因此,开放封闭原则具体具体定义是这样:python

  1. 对扩展是开放的

  咱们说,任何一个程序,不可能在设计之初就已经想好了全部的功能而且将来不作任何更新和修改。因此咱们必须容许代码扩展、添加新功能。数组

  1. 对修改是封闭的

  就像咱们刚刚提到的,由于咱们写的一个函数,颇有可能已经交付给其余人使用了,若是这个时候咱们对函数内部进行修改,或者修改了函数的调用方式,颇有可能影响其余已经在使用该函数的用户。OK,理解了开封封闭原则以后,咱们聊聊装饰器。网络

  什么是装饰器?从字面意思来分析,先说装饰,什么是装饰? 装饰就是添加新的,好比你家刚买的房子,下一步就是按照本身的喜欢的方式设计,进行装修,装饰,地板,墙面,家电等等。什么是器?器就是工具,也是功能,那装饰器就好理解了:就是添加新的功能。闭包

  好比我如今不会飞,怎么才能让我会飞?给我加一个翅膀,我就能飞了。那么你给我加一个翅膀,它会改变我原来的行为么?我以前的吃喝拉撒睡等生活方式都不会改变。它就是在我原来的基础上,添加了一个新的功能。函数

今天咱们讲的装饰器(装修,翅膀)是以功能为导向的,就是一个函数。工具

被装饰的对象:好比毛坯房,我本人,其实也是一个函数。测试

因此装饰器最终最完美的定义就是:在不改变原被装饰的函数的源代码以及调用方式下,为其添加额外的功能。设计


接下来,咱们经过一个例子来为你们讲解这个装饰器:code

需求介绍:你如今xx科技有限公司的开发部分任职,领导给你一个业务需求让你完成:让你写代码测试小明同窗写的函数的执行效率。

import time
def index():
    time.sleep(2)  # 模拟一下网络延迟以及代码的效率
    print('欢迎访问博客园主页')
    
start_time = time.time()
index()
end_time = time.time()
print(f'此函数的执行效率为{end_time-start_time}')

可是你完成的是一个测试其余函数的执行效率的功能,若是让你测试一下,小张,小李,小刘的函数效率呢? 你是否是全得复制

import time
def index():
    time.sleep(2)  # 模拟一下网络延迟以及代码的效率
    print('欢迎访问博客园首页')

def home(name):
    time.sleep(3)  # 模拟一下网络延迟以及代码的效率
    print(f'欢迎访问{name}主页')

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

start_time = time.time()
home('diege')
end_time = time.time()
print(f'此函数的执行效率为{end_time-start_time}')

重复代码太多了,因此要想解决重复代码的问题,怎么作?咱们是否是学过函数,函数就是以功能为导向,减小重复代码。

import time
def index():
    time.sleep(2)  # 模拟一下网络延迟以及代码的效率
    print('欢迎访问博客园主页')

def inner():
    start_time = time.time()
    index()
    end_time = time.time()
    print(f'此函数的执行效率为{end_time-start_time}')

inner()

虽然将测试功能的代码封装成了一个函数,可是这样,你只能测试小明同窗的的函数index,你要是测试其余同事的函数呢?你怎么作?

import time
def index():
    time.sleep(2)  # 模拟一下网络延迟以及代码的效率
    print('欢迎访问博客园主页')

def home(name):
    time.sleep(3)  # 模拟一下网络延迟以及代码的效率
    print(f'欢迎访问{name}主页')

def inner():
    start_time = time.time()
    index()
    home('diege')
    end_time = time.time()
    print(f'此函数的执行效率为{end_time-start_time}')

timer()

要是像上面那么作,每次测试其余同事的代码还须要手动改,这样是否是太low了?因此如何变成动态测试其余函数?咱们是否是学过函数的传参?可否将被装饰函数的函数名做为函数的参数传递进去呢?

import time
def index():
    time.sleep(2)  # 模拟一下网络延迟以及代码的效率
    print('欢迎访问博客园主页')

def home(name):
    time.sleep(3)  # 模拟一下网络延迟以及代码的效率
    print(f'欢迎访问{name}主页')

def timmer(func):  # func == index 函数
    start_time = time.time()
    func()  # index()
    end_time = time.time()
    print(f'此函数的执行效率为{end_time-start_time}')

timmer(index)

这样我将index函数的函数名做为参数传递给timmer函数,而后在timmer函数里面执行index函数,这样就变成动态传参了。好,如今将上面的代码快速练一遍。 练习完了以后,发现有什么问题么? 对比着开放封闭原则说: 首先,index函数除了完成了本身以前的功能,还增长了一个测试执行效率的功能,对不?因此也符合开放原则。 其次,index函数源码改变了么?没有,可是执行方式改变了,因此不符合封闭原则。 原来如何执行? index() 如今如何执行? inner(index),这样会形成什么问题? 假如index在你的项目中被100处调用,那么这相应的100处调用我都得改为inner(index)。很是麻烦,也不符合开放封闭原则。

实现真正的开放封闭原则:装饰器。

只要把那个闭包的执行过程整清楚,那么这个你想不会都难。

import time

def timer(func):  # func = index
    def inner():
        start_time = time.time()
        func()
        end_time = time.time()
        print(f'此函数的执行效率为{end_time-start_time}')
    return inner

# @timer
def index():
    time.sleep(2)  # 模拟一下网络延迟以及代码的效率
    print('欢迎访问博客园主页')
    return '访问成功'

foo = timer(index)
foo()

咱们分析一下代码,代码执行到这一行:foo = timer(index) 先执行谁?看见一个等号先要执行等号右边, timer(index) 执行timer函数将index函数名传给了func形参。内层函数inner执行么?不执行,inner函数返回 给foo变量。因此咱们执行foo() 就至关于执行inner闭包函数。 foo(),这样既测试效率又执行了原函数,有没有问题?固然有啦!!你要解决原函数执行方式不改变的问题,怎么作? 因此你能够把 foo 换成 index变量就完美了! index = timer(index),特别要注意函数外面的index实际是inner函数的内存地址而不是index函数。理解一下,这个timer就是最简单版本装饰器,在不改变原index函数的源码以及调用方式前提下,为其增长了额外的功能,测试执行效率。

下面为修改后代码

import time

def timer(func):  # func = index
    def inner():
        start_time = time.time()
        func()
        end_time = time.time()
        print(f'此函数的执行效率为{end_time-start_time}')
    return inner

# @timer
def index():
    time.sleep(2)  # 模拟一下网络延迟以及代码的效率
    print('欢迎访问博客园主页')
    return '访问成功'

index = timer(index)
index()

用语法糖来实现

import time

def timer(func):  # func = index
    def inner():
        start_time = time.time()
        func()
        end_time = time.time()
        print(f'此函数的执行效率为{end_time-start_time}')
    return inner

@timer
def index():
    time.sleep(2)  # 模拟一下网络延迟以及代码的效率
    print('欢迎访问博客园主页')
    return '访问成功'

#index = timer(index)
index()

有了 @ ,咱们就能够省去index = timer(index)这一句了,直接调用index()便可获得想要的结果。大家看到了没有,index() 函数不须要作任何修改,只需在定义的地方加上装饰器,调用的时候仍是和之前同样,若是咱们有其余的相似函数,咱们能够继续调用装饰器来修饰函数,而不用重复修改函数或者增长新的封装。这样,咱们就提升了程序的可重复利用性,并增长了程序的可读性。

装饰器在 Python 使用如此方便都要归因于 Python 的函数能像普通的对象同样能做为参数传递给其余函数,能够被赋值给其余变量,能够做为返回值,能够被定义在另一个函数内。

拓展连接: https://www.cnblogs.com/jin-xin/articles/10871410.html https://www.runoob.com/w3cnote/python-func-decorators.html

相关文章
相关标签/搜索