初学者python笔记(装饰器、高阶函数、闭包)

一个函数被定义完成后,甚至程序发布后,后期可能需要添加某些功能,但是我们不可能每次都去修改原函数的代码,这时候装饰器就可以上场了,本篇文章将会用一个个可实现的代码,由浅入深、循序渐进得阐述装饰器的强大之处

装饰器定义、作用、原则:

  1. 定义: 一个装饰函数的函数,即为其他函数添加附加功能
  2. 作用: 给函数添加新功能,但是原来的函数代码不会被更改
  3. 原则: 不修改被修饰函数的源代码,不修改被修饰函数的调用方式

装饰器=高阶函数+函数嵌套+闭包

  1. 高阶函数: 函数接收的参数是一个函数名 或者 函数的返回值是一个函数名
  2. 未使用装饰器:
import time
def test(s):
    start_time = time.time()
    res = 0
    for i in s:
        time.sleep(0.1)  #沉睡10秒
        res += i
    stop_time = time.time()
    print('函数的运行时间是{}秒'.format(stop_time - start_time))
    return res
print(test(range(100)))  #运行函数,传入一个从0到100的范围

运行结果:
函数的运行时间是10.012572765350342秒
4950

  1. 高阶函数:
import time
def foo():
    time.sleep(3)
    print('大海如此宽广,总有一天你会遇见真心的伙伴')
def test(func):  #传入的参数是一个函数,即为高阶函数
    print(func)  #输出函数的内存地址
    func()  #调用形参函数
test(foo)  #调用高阶函数

运行结果:
<function foo at 0x02BF7B70>
大海如此宽广,总有一天你会遇见真心的伙伴

  1. 带修饰的高阶函数(不修改原函数源代码):
import time
def foo():
    time.sleep(3)
    print('大海如此宽广,总有一天你会遇见真心的伙伴')
def test(func):  #传入的参数是一个函数,即为高阶函数
    start_time = time.time()
    print(func)  #输出函数的内存地址
    func()  #调用形参函数
    stop_time = time.time()
    print('函数运行时间为 %s' %(stop_time - start_time))
test(foo)  #调用高阶函数

运行结果:
<function foo at 0x004DC5D0>
大海如此宽广,总有一天你会遇见真心的伙伴
函数运行时间为 3.0281732082366943

  1. 带修饰的高阶函数(不修改原函数调用方式):
import time
def foo():
    time.sleep(3)
    print('大海如此宽广,总有一天你会遇见真心的伙伴')
def test(func):  #传入的参数是一个函数,即为高阶函数
    return func  #函数的返回值为函数名,也称为高阶函数
foo = test(foo)  #用foo接收test运行的返回值
foo()

运行结果:
大海如此宽广,总有一天你会遇见真心的伙伴

  1. 不修改源代码也不修改调用方式:
import time
def foo():
    time.sleep(3)
    print('大海如此宽广,总有一天你会遇见真心的伙伴')
def timmer(func):  #作为一个修饰函数
    start_time = time.time()
    func()
    stop_time = time.time()
    print('函数运行时间为 %s' %(stop_time - start_time))
    return func  #函数的返回值为函数名,也称为高阶函数
foo = timmer(foo)  #将原函数名作为接收的变量,被修饰后,就不会修改原函数的调用方式
foo()
#这样调用后,其实是会连带被修饰的结果一起输出,因为上一句赋值时已经调用了修饰函数

运行结果:
大海如此宽广,总有一天你会遇见真心的伙伴
函数运行时间为 3.018172264099121
大海如此宽广,总有一天你会遇见真心的伙伴

但是这样,它仅仅用了高阶函数,还是满足不了装饰器的功能,因为它多运行了一步

  1. 函数嵌套:
    一个函数里定义多个函数(这里需要用到函数作用域和函数嵌套的知识,在我的这篇文章里有从浅入深的详细分析:初学者python笔记(函数)
def father(name):
    #name = '白胡子_1'  #三个地方对局部变量的修改
    def son():
        #name = '白胡子_2'
        def grandson():
            #name = '白胡子_3'
            print('我的老爹是{}'.format(name))  #离它最近的局部变量的修改
        grandson()  
    son()  #运行嵌套函数
father('白胡子')
#里面的变量是一个一个层级的关系,从形参,可以一直传到底层的函数中再调用

运行结果:
我的老爹是白胡子

  1. 基本装饰器的实现(带语法糖):
import time
def timmer(func):
    def wrap():
        start_time = time.time()  #给装饰器加的功能
        func()  #在装饰器中运行函数
        stop_time = time.time()
        print('函数运行了{}秒'.format(stop_time - start_time))
    return wrap  #函数的返回值是函数,所以为高阶函数
@timmer  #语法糖的使用,使原函数即foo函数被装饰。相当于foo = timmer(foo)语句
def foo():
    time.sleep(3)
    print('test函数运行完毕!')
    
foo()  #运行原函数,方式不变,满足原则

运行结果:
test函数运行完毕!
函数运行了3.0251729488372803秒

  1. 能得到原函数返回值的装饰器:
import time
def timmer(func):
    def wrap():
        start_time = time.time()  #给装饰器加的功能
        res = func()  #在装饰器中运行函数,并且用一个变量来接收以返回原函数的返回值
        stop_time = time.time()
        print('函数运行了{}秒'.format(stop_time - start_time))
        return res  #返回原函数的返回值
    return wrap  #函数的返回值是函数,所以为高阶函数
@timmer  #语法糖的使用,使原函数即foo函数被装饰。相当于foo = timmer(foo)语句
def foo():
    time.sleep(3)
    print('test函数运行完毕!')
    return '这是test的返回值'  #为了实现把原函数的返回值也接收到
    
foo()  #运行原函数,方式不变,满足原则
print(foo())  #输出原函数的返回值

运行结果:
test函数运行完毕!
函数运行了3.0211730003356934秒
test函数运行完毕!
函数运行了3.0581750869750977秒
这是test的返回值

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章