Python 装饰器

直接进入主题

原代码

以下是原代码,要求给改代码添加统计时间功能

 

import time

def say_hello():
    time.sleep(1)
    print("Hi!")

say_hello()

版本1(直接在原函数上修改)

可能有的同学就做出了下面这个版本

import time

def say_hello():
    start_time=time.time()
    time.sleep(1)
    print("Hi!")
    stop_time=time.time()
    print('该函数运行时间为:%s' %(stop_time-start_time))

say_hello()
#输出:
# Hi!
# 该函数运行时间为:1.0008747577667236

  该版本存在的问题

  1. 直接修改了函数的原始内容
  2. 当如果有第二个函数需要实现相同的功能是,需要重新做一遍重复的工作

版本2(将函数当做参数传入)

  经过修改上面的版本我们做出了新的代码

import time

def run_time(func):
    start_time=time.time()
    func()
    stop_time=time.time()
    print('该函数运行时间为:%s' %(stop_time-start_time))

def say_hello():
    time.sleep(1)
    print("Hi!")

run_time(say_hello)

 

  该版本通过定义一个新的函数 run_time ,然后再新函数中调用原来的函数,实现计算函数的运行时间功能

  但是该版本有个问题就是修改了原函数的调用方式,这样很不好。

版本3(修改原函数的内容)

import time

def run_time(func):
    def wrapper():
        start_time=time.time()
        func()
        stop_time=time.time()
        print('该函数运行时间为:%s' %(stop_time-start_time))
    return  wrapper


def say_hello():
    time.sleep(1)
    print("Hi!")

say_hello=run_time(say_hello)

say_hello()

  于是乎我们就有了以上的新代码,该代码即没有修改原函数,也没有修改函数的调用方式。

  巧妙的通过返回函数 wrapper ,在 执行say_hello=run_time(say_hello)  这句代码将 say_hello 的内容修改。

  这基本上算是Python中装饰器的基本架子了。

  再把  say_hello=run_time(say_hello)  这句代码优化下就变为下面的版本了。

版本4(关键词@)

import time

def run_time(func):
    def wrapper():
        start_time=time.time()
        func()
        stop_time=time.time()
        print('该函数运行时间为:%s' %(stop_time-start_time))
    return  wrapper

@run_time #  这句代码等同于  say_hello=run_time(say_hello)
def say_hello():
    time.sleep(1)
    print("Hi!")

say_hello()

 

  这个版本的代码算是一个装饰器了,但是我们又有了一个问题,上面的原函数是没有参数,没有返回值的,如果有参数有返回值又该怎么解决呢?请看下一个版本代码

版本5(原函数带有参数、返回值时的实现)

  这个版本通过修改 wrapper 函数的参数,并将参数传入 run_time 中传入的 func 函数中,实现了带有参数的调用。

  并给wrapper 函数添加 func 函数执行后的返回值。完美实现了需求。

import time

def run_time(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        res = func(*args, **kwargs)
        stop_time = time.time()
        print('该函数运行时间为:%s' % (stop_time - start_time))
        return res
    return wrapper

@run_time  # 这句代码等同于  say_hello=run_time(say_hello)
def say_hello(name):
    time.sleep(1)
    print(name, ":Hi!")
    return "我是返回值"

print(say_hello("Oliver"))

# Oliver :Hi!
# 该函数运行时间为:1.000196933746338
# 我是返回值

 

  现在问题又来了,如果某些函数需要直接输出运行时间,而某些函数需要将运行时间写入文件又该怎么处理呢?

版本6(装饰器函数需要参数时实现)

  当然有的同学可能会说就写两个装饰器函数呗,但是因为装饰器函数实现的功能基本一致,而如果能想办法给装饰器函数添加一个参数就完美解决这个问题了。请看以下代码

import time

def run_time(type):
    def deco(func):
        def wrapper(*args, **kwargs):
            start_time = time.time()
            res = func(*args, **kwargs)
            stop_time = time.time()
            content = '该函数运行时间为:%s' % (stop_time - start_time)
            if type == "print":
                print(content)
            elif type == "write_file":
                with open("运行时间记录", "a+", encoding='utf8') as f:
                    f.write(
                        '{0} {1}:{2}\r\n'.format(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()), func.__name__,
                                                 content))
            return res

        return wrapper

    return deco


@run_time("print")  #这段代码相当于 say_hello= run_time("print")(say_hello) ---》也就是  say_hello= deco(say_hello)
def say_hello(name):
    time.sleep(1)
    print(name, ":Hi!")
    return "我是返回值"


@run_time("write_file")
def say_hello2(name):
    time.sleep(1)
    print(name, ":你好!")
    return "我是say_hello2返回值"


print(say_hello("Oliver"))
print(say_hello2("张三"))
# 输出:
# Oliver :Hi!
# 该函数运行时间为:1.0008816719055176
# 我是返回值
# 张三 :你好!
# 我是say_hello2返回值

# 文件内容:
# 2018-08-03 10:54:37 say_hello2:该函数运行时间为:1.0004534721374512

版本7(一个函数多个装饰器)

  当然一个函数也是可以添加多个装饰器的。比如添加验证功能,并且计算函数运行时间。执行的顺序也是从下往上执行的

如下代码:

import time

def verify(func):
    def wrapper(*args, **kwargs):
        print("验证一些信息···")
        res = func(*args, **kwargs)
        return res
    return wrapper

def run_time(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        res = func(*args, **kwargs)
        stop_time = time.time()
        print('该函数运行时间为:%s' % (stop_time - start_time))
        return res
    return wrapper

@verify
@run_time
def say_hello(name):
    time.sleep(1)
    print(name ,":Hi!")

say_hello('Oliver')

#输出:
# 验证一些信息···
# Oliver :Hi!
# 该函数运行时间为:1.0001380443572998

 

 

 

posted @ 2018-08-03 11:07  下-个路口  阅读(152)  评论(0编辑  收藏  举报