Python 函數與常用模組 - 裝飾器(小高潮)

裝飾器 - 小高潮

請使用下面的代碼,再不修改源代碼下,替foo函數跟bar函數新增一個功能

#!/usr/bin/env python3
# -*- coding:utf-8 -*-

import time

def foo():
    time.sleep(3)
    print('in the foo')

def bar():
    time.sleep(3)
    print('int the bar')

foo()
bar()

還記得之前說的裝飾器前奏(高階函數)的規則嗎?

  • 規則1: 在不修改被裝飾函數源代碼的情況下,為它加上新的功能
  • 規則2: 不能修改被裝飾的函數的調用方式

首先,定義一個函數,來做為新功能的實現。

#!/usr/bin/env python3
# -*- coding:utf-8 -*-

import time

def deco(func):
    start_time = time.time()
    func()
    stop_time = time.time()
    print("in the func run time is {}".format(stop_time - start_time))

def foo():
    time.sleep(3)
    print('in the foo')

def bar():
    time.sleep(3)
    print('int the bar')

# deco(foo()) 
# deco(bar())
# 這樣的寫法的錯的,因為如果是加了小括號()代表是把這個函數的執行結果的返回值給傳進去了,所以這樣的寫法是不行的。

deco(foo)
deco(bar)

---------------執行結果---------------

in the foo
in the func run time is 3.0011279582977295
int the bar
in the func run time is 3.0044898986816406

Process finished with exit code 0

在上面的代碼中,聲明一個新的函數叫 deco(func),主要是實現的功能是 顯示程序執行多久時間,調用方式使用 deco(foo),這樣會把 foo函數 給帶進去 deco(func),但使用這樣的方式,雖然有達成 規則1: 在不修改被裝飾函數源代碼的情況下,為它加上新的功能,卻違反了 規則2: 不能修改被裝飾的函數的調用方式,所以我們再修改一下代碼

#!/usr/bin/env python3
# -*- coding:utf-8 -*-


import time

def deco(func):
    start_time = time.time()
    return func
    stop_time = time.time()
    print("in the func run time is {}".format(stop_time - start_time))

def foo():
    time.sleep(3)
    print('in the foo')

def bar():
    time.sleep(3)
    print('in the bar')

foo = deco(foo)
foo()
bar = deco(bar)
bar()

---------------執行結果---------------

in the foo
in the bar

Process finished with exit code 0

結果這上面代碼執行完後,有發現什麼問題嗎?沒錯,雖然有符合 規則2: 不能修改被裝飾的函數的調用方式了,但原本新增的功能卻不見了,這是為什麼呢?請看圖解,先看綠色的線

那要怎麼把新功能給加上去?在加上新功能之前,還記得之前說是由高階函數 + 嵌套函數 => 裝飾器,而上面的代碼都只有使用到高階函數,並沒有使用到嵌套函數,接下來就要使用嵌套函數去實現新功能。

#!/usr/bin/env python3
# -*- coding:utf-8 -*-


import time

def timer(func):
    def deco():
        start_time = time.time()
        func()
        stop_time = time.time()
        print("in the func run time is {}".format(stop_time - start_time))
    return deco

def foo():
    time.sleep(3)
    print('in the foo')

def bar():
    time.sleep(3)
    print('in the bar')

print("打印return deco = timer(foo)的記憶體位址: {}".format(timer(foo)))
print("打印return deco = timer(bar)的記憶體位址: {}".format(timer(bar)))

foo = timer(foo)
foo()
bar = timer(bar)
bar()

---------------執行結果---------------

打印return deco = timer(foo)的記憶體位址: <function timer.<locals>.deco at 0x109e8e6a8>
打印return deco = timer(bar)的記憶體位址: <function timer.<locals>.deco at 0x109e8e6a8>
in the foo
in the func run time is 3.00457501411438
in the bar
in the func run time is 3.0016891956329346

Process finished with exit code 0

請看圖解

首先,我們來檢視一下,foo函數的代碼都沒有被修改,而且調用方式也沒有被修改,所以我們可以很確定地說,這已經完全符合 裝飾器 所有的規則。

但這樣每次都還要去運行 foo = timer(foo),所以Python有提供了一個語法糖,只要在要 foo函數 前加一個 @timer 就行了

#!/usr/bin/env python3
# -*- coding:utf-8 -*-


import time

def timer(func):
    def deco():
        start_time = time.time()
        func()
        stop_time = time.time()
        print("in the func run time is {}".format(stop_time - start_time))
    return deco

@timer  # foo = timer(foo)
def foo():
    time.sleep(3)
    print('in the foo')
@timer  # bar = timer(bar)
def bar():
    time.sleep(3)
    print('in the bar')

print("打印return deco = timer(foo)的記憶體位址: {}".format(timer(foo)))
print("打印return deco = timer(bar)的記憶體位址: {}".format(timer(bar)))

foo()
bar()

---------------執行結果---------------

打印return deco = timer(foo)的記憶體位址: <function timer.<locals>.deco at 0x10d743730>
打印return deco = timer(bar)的記憶體位址: <function timer.<locals>.deco at 0x10d743730>
in the foo
in the func run time is 3.0012028217315674
in the bar
in the func run time is 3.0002009868621826

Process finished with exit code 0

以上就是 裝飾器 的功能實現。

posted @ 2017-06-12 23:19  丹尼爾  阅读(171)  评论(0编辑  收藏  举报