9-4 如何实现属性可修改的函数装饰器

一、实现

from functools import wraps
import time
import logging

def warn(timeout):      #带参数的装饰器,也就是内部能返回一个装饰器
    def decorator(func): #定义一个真正的函数装饰器,接受一个函数参数
        def wrapper(*args,**kargs): #在装饰器函数内部定义一个包裹函数
            #统计运行时间
            start = time.time()   #记录func运行前的时间
            res = func(*args,**kargs)
            used = time.time()-start
            if used >timeout:    #如果输出超时导入日志当中
                msg = '"%s":%s > %s' %(func.__name__,used,timeout)  #定义消息格式,函数名,使用时间,超时时间
                logging.warn(msg)  #使用标准库函数记录日志
            return res             #包裹函数要返回被包裹函数
        return wrapper      #装饰器内部返回wrapper包裹函数
        pass

    return decorator  #返回一个装饰器

测试

from random import randint
@warn(1.5)     #装饰器,设置超时时间1.5s
def test():
    print('In test')
    while randint(0,1):    #50%概率进入睡眠
        time.sleep(0.5)    #睡眠500ms
    
for _ in xrange(30):      #循环30次运行test
test()

输出结果:

In test

In test

In test

In test

In test

In test

In test

In test

In test

In test

In test

In test

In test

In test

WARNING:root:"test":2.56699991226 > 1.5

In test

In test

In test

In test

WARNING:root:"test":2.06299996376 > 1.5

In test

In test

In test

In test

In test

In test

In test

In test

In test

In test

In test

WARNING:root:"test":1.50699996948 > 1.5

In test

WARNING:root:"test":1.50499987602 > 1.5

>>> 

二、实现动态修改timeout功能

from functools import wraps
import time
import logging

def warn(timeout):      #带参数的装饰器,也就是内部能返回一个装饰器
    def decorator(func): #定义一个真正的函数装饰器,接受一个函数参数
        def wrapper(*args,**kargs): #在装饰器函数内部定义一个包裹函数
            #统计运行时间
            start = time.time()   #记录func运行前的时间
            res = func(*args,**kargs)
            used = time.time()-start
            if used >timeout:    #如果输出超时导入日志当中
                msg = '"%s":%s > %s' %(func.__name__,used,timeout)  #定义消息格式,函数名,使用时间,超时时间
                logging.warn(msg)  #使用标准库函数记录日志
            return res             #包裹函数要返回被包裹函数

        def setTimeout(k):       #在装饰器内部定义一个新的函数,
            #timeout 是在wrapper闭包使用的自由变量,在这个函数不能直接对timeout进行修改
            #这里py3修改timeout值会生成一个局部的变量 。可以使用 nonlocal timeout 来声明一个嵌套作用域下的变量
            #py2可以读嵌套作用域的值,但不能修改修改会报错“找不到变量”,没有nonlocal 关键字就无法实现修改timeout变量对象的引用
            #但py2可以把timeout实现一个可变对象 ,通过用可变对象修改的方式来实现。
            nonlocal timeout
            timeout = k
            
        wrapper.setTimeout = setTimeout   #setTimeout()函数做为wrapper()函数的属性,
                                #未来客户就可以通过函数来调用setTimeout(),从而修改timeout的值。

        return wrapper      #装饰器内部返回wrapper包裹函数
        pass

    return decorator  #返回一个装饰器

测试

from random import randint
@warn(1.5)     #装饰器,设置超时时间1.5s
def test():
    print('In test')
    while randint(0,1):    #50%概率进入睡眠
        time.sleep(0.5)    #睡眠500ms
    
for _ in range(30):      #循环30次运行test
    test()

test.setTimeout(1)
for _ in r ange(30):      #循环30次运行test
    test()    

输出结果:

 py2可以读嵌套作用域的值,但不能修改修改会报错“找不到变量”,没有nonlocal 关键字就无法实现修改timeout变量对象的引用。但py2可以把timeout实现一个可变对象 ,通过用可变对象修改的方式来实现。

下面是python2实现方法

# -*- coding: cp936 -*-

from functools import wraps
import time
import logging

def warn(timeout):      #带参数的装饰器,也就是内部能返回一个装饰器
    timeout = [timeout] #py2不支持nonlocal关键字,把变量放入列表中
    def decorator(func): #定义一个真正的函数装饰器,接受一个函数参数
        def wrapper(*args,**kargs): #在装饰器函数内部定义一个包裹函数
            #统计运行时间
            start = time.time()   #记录func运行前的时间
            res = func(*args,**kargs)
            used = time.time()-start
            if used >timeout[0]:    #如果输出超时导入日志当中
                msg = '"%s":%s > %s' %(func.__name__,used,timeout[0])  #定义消息格式,函数名,使用时间,超时时间
                logging.warn(msg)  #使用标准库函数记录日志
            return res             #包裹函数要返回被包裹函数

        def setTimeout(k):       #在装饰器内部定义一个新的函数,
            #timeout 是在wrapper闭包使用的自由变量,在这个函数不能直接对timeout进行修改
            #这里py3修改timeout值会生成一个局部的变量 。可以使用 nonlocal timeout 来声明一个嵌套作用域下的变量
            #py2可以读嵌套作用域的值,但不能修改修改会报错“找不到变量”,没有nonlocal 关键字就无法实现修改timeout变量对象的引用
            #但py2可以把timeout实现一个可变对象 ,通过用可变对象修改的方式来实现。
            #nonlocal timeout
            #timeout = k
            timeout[0] = k
            
            
        wrapper.setTimeout = setTimeout   #setTimeout()函数做为wrapper()函数的属性,
                                #未来客户就可以通过函数来调用setTimeout(),从而修改timeout的值。

        return wrapper      #装饰器内部返回wrapper包裹函数
        pass

    return decorator  #返回一个装饰器

测试

from random import randint
@warn(1.5)     #装饰器,设置超时时间1.5s
def test():
    print('In test')
    while randint(0,1):    #50%概率进入睡眠
        time.sleep(0.5)    #睡眠500ms
    
for _ in range(30):      #循环30次运行test
    test()

test.setTimeout(1)
for _ in range(30):      #循环30次运行test
    test()    

输出结果:

In test

WARNING:root:"test":2.0039999485 > 1.5

In test

In test

In test

In test

In test

In test

In test

WARNING:root:"test":2.00299978256 > 1.5

In test

In test

In test

In test

In test

In test

WARNING:root:"test":1.00300002098 > 1

In test

In test

In test

In test

In test

WARNING:root:"test":1.05200004578 > 1

In test

WARNING:root:"test":1.0039999485 > 1

In test

WARNING:root:"test":1.00400018692 > 1

In test

posted on 2018-05-10 09:44  石中玉smulngy  阅读(176)  评论(0编辑  收藏  举报

导航