闭包与装饰器

什么是闭包呢? Closure

如果在一个函数的内部函数中,有对外部作用域的一个引用,那么内部函数就是一个闭包。在使用时,可以直接调用内部函数,内部函数也照样可以使用外部函数的变量。

例如:

def outter():
    a = 1 # a为inner的外部作用域

    def inner():
        print(a)

    return inner


outter()() # 直接调用inner()

打印结果为1

什么是装饰器? Decorator

装饰器就是在给原有的函数加装饰。在不修改原有函数的基础上添加原有函数的功能。

比如:

原有功能函数method1、method2,要给原有功能函数添加计算函数执行时间的功能,并且不能修改原有函数,因为原有函数可能已经在很多地方使用,轻易修改的话可能会造成系统崩溃。

这时,我们就可以用闭包的方法来来给函数添加新功能。

举例:

原有函数:

import time


def method01():
    time.sleep(2)
    print('method1')
    pass


def method02():
    time.sleep(3)
    print('method2')
    pass

现在要给这两个函数添加计算执行时间。

def perform_time(func):
    start_time = time.time()
    func()
    end_time = time.time()
    print(end_time - start_time)

perform_time(method1)
perform_time(method2)

虽然,这样也完成了,但是每个函数都要这样调用,太麻烦了。有没有更简单点的办法呢?其实,我们可以使用闭包的方法来完成。

def perform_time(func):
    """
    使用闭包的方法,也就是装饰器方法。
    func作为inner()的外部变量调用。
    """
    def inner():
        start_time = time.time()
        func() # inner调用外部资源
        end_time = time.time()
        print(end_time - start_time)
    # 返回内部函数
    return inner 

method01()
print('===================================')
method01 = perform_time(method01)
# 将perform_time(method01) 赋值给method1,实际上是将method01指向perform_time(method01)的内存地址。以后调用method01()就可以得到函数的执行结果。
method01()

执行结果为:

Connected to pydev debugger (build 182.5107.22)
method1
===================================
method1
2.0030062198638916

Process finished with exit code 0

我们可以使用装饰器的写法@

在method2函数的上面添加@perform_time, 这句话的意思其实就是执行 method01 = perform_time(method01)

@perform_time
def method02():
    time.sleep(3)
    print('method2')
    pass


method01()
print('===================================')
method01 = perform_time(method01)
method01()
print('===================================')
method02()

执行结果为:

Connected to pydev debugger (build 182.5107.22)
method1
===================================
method1
2.0060038566589355
===================================
method2
3.006004810333252

Process finished with exit code 0
 

这样,我们的代码更加简洁,我们也完成了在不修改原有代码的基础上,添加新功能。这就是装饰器的作用。

接着,我们还有带参数的函数也需要加入计算时间。

def add(*args, **kwargs):
    """
    计算传入参数的累加
    """
    ret = 0
    if args:
        # 元组处理
        for arg in args:
            ret += arg
    elif kwargs:
        # 字典处理
        for arg in kwargs.values():
            ret += arg

    return ret

修改装饰器函数perform_time

def perform_time(func):

    def inner(*args, **kwargs): # 传入任意参数
        start_time = time.time()
        ret = func(*args, **kwargs) # 记录函数返回值
        end_time = time.time()
        print(end_time - start_time)
        return ret # 返回值

    return inner

 最后在add函数上加上装饰器

@perform_time

@perform_time
def add(*args, **kwargs):
    ret = 0
    if args:
        for arg in args:
            ret += arg
    elif kwargs:
        for arg in kwargs.values():
            ret += arg
    return ret


method01()
print('===================================')
method01 = perform_time(method01)
method01() # 无参数,无返回值
print('===================================')
method02() # 无参数,无返回值
print('===================================')
print(add(1, 2, 3, 4, 5)) # 传入元组参数
print(add(a=1, b=2, c=3, d=4, e=6)) # 传入字典参数

 返回结果:

Connected to pydev debugger (build 182.5107.22)
method1
===================================
method1 # 无参数无返回值,不影响结果
2.0001144409179688
===================================
method2 
3.000171661376953
===================================
0.0 # 耗时
15 
0.0 # 耗时
16

Process finished with exit code 0

带参数的装饰器:

现在,我们需要给某些函数添加日志,某些函数不需要添加日志。这时,我们就需要用到带参数的装饰器。

为了让装饰器能够接收是否添加日志参数,需要在原有装饰器外,再嵌套一层函数。

def perform_time_logger(is_log=False):

    def perform_time(func):

        def inner(*args, **kwargs):
            start_time = time.time()
            ret = func(*args, **kwargs)
            end_time = time.time()
            print(end_time - start_time)
            # 判断是否打印日志
            if is_log:
                # 获取参数
                if args or kwargs:
                    if kwargs:
                        args = kwargs
                else:
                    args = ''
                # 打印日志
                print('function: {0} has been performed with parameters {2} , on time {1} .'.format(func.__name__, time.ctime(start_time), args))
            return ret
        return inner

    return perform_time

这是需要将所有@perform_time修改为@perform_time_logger(), 如果需要打印日志就将参数设置为True,不打印就无需添加参数。

@perform_time_logger(is_log=True)
def method01():
    time.sleep(2)
    print('method1')
    pass


@perform_time_logger()
def method02():
    time.sleep(3)
    print('method2')
    pass


@perform_time_logger(True)
def add(*args, **kwargs):
    ret = 0
    if args:
        for arg in args:
            ret += arg
    elif kwargs:
        for arg in kwargs.values():
            ret += arg
    return ret


print('===================================')
method01()
print('===================================')
method02()
print('===================================')
print(add(1, 2, 3, 4, 5))
print(add(a=1, b=2, c=3, d=4, e=6))

执行结果:

 

Connected to pydev debugger (build 182.5107.22)
===================================
method1
2.0001144409179688
function: method01 has been performed with parameters  , on time Mon Jan  7 23:06:49 2019 .
===================================
method2
3.000171661376953
===================================
0.0
function: add has been performed with parameters (1, 2, 3, 4, 5) , on time Mon Jan  7 23:06:54 2019 .
15
0.0
function: add has been performed with parameters {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 6} , on time Mon Jan  7 23:06:54 2019 .
16

Process finished with exit code 0

这样,装饰器就告一段落了。

 

附完整代码:

# _*_ coding: utf-8 _*_
__author__ = 'jevoly'


import time


# def perform_time(func):
#     start_time = time.time()
#     func()
#     end_time = time.time()
#     print(end_time - start_time)

def perform_time_logger(is_log=False):

    def perform_time(func):

        def inner(*args, **kwargs):
            start_time = time.time()
            ret = func(*args, **kwargs)
            end_time = time.time()
            print(end_time - start_time)
            if is_log:
                if args or kwargs:
                    if kwargs:
                        args = kwargs
                else:
                    args = ''
                print('function: {0} has been performed with parameters {2} , on time {1} .'.format(func.__name__, time.ctime(start_time), args))
            return ret
        return inner

    return perform_time


@perform_time_logger(is_log=True)
def method01():
    time.sleep(2)
    print('method1')
    pass


@perform_time_logger()
def method02():
    time.sleep(3)
    print('method2')
    pass


@perform_time_logger(True)
def add(*args, **kwargs):
    ret = 0
    if args:
        for arg in args:
            ret += arg
    elif kwargs:
        for arg in kwargs.values():
            ret += arg
    return ret


print('===================================')
method01()
print('===================================')
method02()
print('===================================')
print(add(1, 2, 3, 4, 5))
print(add(a=1, b=2, c=3, d=4, e=6))

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值