python装饰器理解,小盒子,中盒子,大盒子

“”"
装饰器目的是在不改变待装饰函数代码的情况下,增加额外的
功能,装饰器的返回值是已装饰的函数对象
“”"

假设有代码:

import time
def test():
    time.sleep(2)
    print("test is running!")
test()

很显然,这段代码运行的结果一定是:等待约2秒后,输出

“”"
高阶函数
那么对于高阶函数的形式可以有两种:

1.把一个函数名当作实参传给另外一个函数(“实参高阶函数”)
2.返回值中包含函数名(“返回值高阶函数”)
“”"

“”"
那么这里面所说的函数名,实际上就是函数的地址,也可以认为是函数的一个标签而已,
并不是调用,是个名词。如果可以把函数名当做实参,那么也就是说可以把函数传递到另一个函数,
然后在另一个函数里面做一些操作,根据这些分析来看,这岂不是满足了装饰器三原则中的第一条,
即不修改源代码而增加功能。那我们看来一下具体的做法:
“”"

import time
def test():
    time.sleep(2)
    print("test is running!")

def deco(func):
    start = time.time()
    func()  # 2
    stop = time.time()
    print(stop - start)
deco(test)  # 1

“”"
我们来看一下这段代码,在#1处,
我们把test当作实参传递给形参func,即func=test。
注意,这里传递的是地址,也就是此时func也指向了之前test所定义的那个函数体,
可以说在deco()内部,func就是test。在#2处,把函数名后面加上括号,就是对函数的调用(执行它)。
因此,这段代码运行结果是:
test is running!
the run time is 3.0009405612945557
“”"

“”"
我们看到似乎是达到了需求,即执行了源程序,
同时也附加了计时功能,但是这只满足了原则1(不能修改被装饰的函数的源代码),
但这修改了调用方式。假设不修改调用方式,那么在这样的程序中,
被装饰函数就无法传递到另一个装饰函数中去。

那么再思考,如果不修改调用方式,就是一定要有test()这条语句,那么就用到了第二种高阶函数,即返回值中包含函数名
“”"

import time


def test():
    time.sleep(2)
    print("test is running!")


def deco(func):
    print(func)
    return func


t = deco(test)  # 3

t()#4

test()

“”“进化版”""

import time

def timer(func):# 5
    def deco():
        start = time.time()
        func()
        stop = time.time()
        print(stop - start)
    return deco

test = timer(test)  # 6
def test():
    time.sleep(2)
    print("test is running!")
test()  # 7

“”"
这段代码可能会有些困惑,怎么忽然多了这么多,暂且先接受它,分析一下再来说为什么是这样。
首先,在#6处,把test作为参数传递给了timer(),此时,在timer()内部,func = test,接下来,
定义了一个deco()函数,当并未调用,只是在内存中保存了,并且标签为deco。在timer()函数的最后返回deco()的地址deco。
然后再把deco赋值给了test,那么此时test已经不是原来的test了,也就是test原来的那些函数体的标签换掉了,换成了deco。
那么在#7处调用的实际上是deco()。

那么这段代码在本质上是修改了调用函数,但在表面上并未修改调用方式,而且实现了附加功能。
那么通俗一点的理解就是:

把函数看成是盒子,test是小盒子,deco是中盒子,timer是大盒子。
程序中,把小盒子test传递到大盒子temer中的中盒子deco,然后再把中盒子deco拿出来,打开看看(调用)
“”"

“”"
3、 真正的装饰器
根据以上分析,装饰器在装饰时,需要在每个函数前面加上:
test = timer(test)
显然有些麻烦,Python提供了一种语法糖,即:
@timer
“”"

“”“实例”""

# def dec(f):
#     n = 3
#     def wrapper(*args, **kw):
#         return f(*args, **kw) * n
#     return wrapper
#
#
# @dec
# def foo(n):
#     return n * 2
#
# print(foo(2))

# 装饰器有参数
# import time
#
# def timer(func):
#     def deco():
#         start = time.time()
#         func()
#         stop = time.time()
#         print(stop-start)
#     return deco
#
# @timer
# def test(parameter): #8
#     time.sleep(2)
#     print("test is running!")
# test()

“”"
对于一个实际问题,往往是有参数的,如果要在#8处,给被修饰函数加上参数,显然这段程序会报错的。
错误原因是test()在调用的时候缺少了一个位置参数的。而我们知道test = func = deco,因此test()=func()=deco()
,那么当test(parameter)有参数时,就必须给func()和deco()也加上参数,
为了使程序更加有扩展性,因此在装饰器中的deco()和func(),加如了可变参数*agrs和 **kwargs。
“”"

import time

def timer(func):
    def deco(*args, **kwargs):
        start = time.time()
        func(*args, **kwargs)
        stop = time.time()
        print(stop-start)
    return deco

@timer
def test(parameter): #8
    time.sleep(2)
    print("test is running!")
test()

“”"
带返回值
“”"

import time

def timer(func):
    def deco(*args, **kwargs):
        start = time.time()
        res = func(*args, **kwargs)
        stop = time.time()
        print(stop-start)
        return res
    return deco

@timer
def test(parameter): #8
    time.sleep(2)
    print("test is running!")
    return "Returned value"
test()

总结:

1, 把函数看成是盒子,test是小盒子,deco是中盒子,timer是大盒子。
程序中,把小盒子test传递到大盒子temer中的中盒子deco,然后再把中盒子deco拿出来,打开看看(调用)‘

2, 根据以上分析,装饰器在装饰时,需要在每个函数前面加上:
test = timer(test)
显然有些麻烦,Python提供了一种语法糖,即:
@timer’

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值