1. 程式人生 > >函式進階四(裝飾器、內建函式)

函式進階四(裝飾器、內建函式)

. 昨日內容回顧

生成器,本質就是迭代器,自己用Python程式碼構建的迭代器
1.生成器函式(yield)
2.生成器表示式(i for i in range(21))

生成器物件:gentor
next(gentor)
send(gentor)
for 迴圈

yield 與 return 的區別

send:
給上一個yield傳送一個值
send 對應yield取值

next:
對應一個yield取值

yield from——將一個可迭代物件變成一個迭代器返回

列表推導式
迴圈模式:[變數(加工後的變數) for 變數 in iterable]
篩選模式:[變數(加工後的變數) for 變數 in iterable if 條件]
三元模式:["*" if a > b else 變數 for 變數 in iterable]

優點:
一行程式碼搞定
缺點:不能使用debug模式查錯

生成器表示式:與列表推導式幾乎一樣,只需將[]改為()即可

字典推導式、集合推導式


二. 裝飾器

# 裝飾器

# 假設現在要寫一個程式來測試某段程式碼的執行效率
import time
# 這是要測試的程式碼段,一個函式
def func():
    print("測試這個函式的執行效率...")
    # 因為該函式幾乎沒什麼程式碼,所以加個延時
    time.sleep(0.2)

func()

# 現在開始先測試程式
def timmer():
    start_time = time.time()
    func()  # 注意是函式呼叫開始計時
    end_time = time.time()
    print("該函式的執行效率%s" % (end_time - start_time))

timmer()

# 第一版完成 def timmer(f): start_time = time.time() f() # 注意是函式呼叫開始計時 end_time = time.time() print("該函式的執行效率%s" % (end_time-start_time)) timmer(func) # 上面這樣只能測試一個人的,如果要測試三個人的,又得這樣: def func1(): print("測試這個函式的執行效率...") # 因為該函式幾乎沒什麼程式碼,所以加個延時 time.sleep(0.2) def func2():
print("測試這個函式的執行效率...") # 因為該函式幾乎沒什麼程式碼,所以加個延時 time.sleep(0.2) def func3(): print("測試這個函式的執行效率...") # 因為該函式幾乎沒什麼程式碼,所以加個延時 time.sleep(0.2) # timmer(func1) # timmer(func2) # timmer(func3) # 上一個版本中,假如有500個函式的專案,那麼專案中可能出現5000次呼叫 # 如果想要不影響專案正常運作的情況下測試每個函式的執行效率,意味著將專案中5000次這樣的呼叫方式func()變成timmer(func)


# 這樣明顯不行,要儘量不改變原函式的呼叫方式,並且能對原函式進行效率測試
# 即原函式既要正常執行,又能測試它的時間

import time
def func():
    print("測試這個函式的執行效率...")
    time.sleep(0.2)

def timmer(f):
    start_time = time.time()
    f()
    end_time = time.time()
    print("該函式的執行效率是%s" % (end_time - start_time))

func()
f1 = func  # 這裡表示 f1 和 func 在記憶體中指向同一個記憶體地址
func = timmer  # 這裡開始,func 指向函式 timmer 在記憶體中的地址,但是 f1 還是指向原先 func 函式的記憶體地址
func(f1)  # 因此相當於timmer(func),沒有返回值,相當於做了一個偽裝

# 上一個版本雖然做了偽裝,執行原函式的呼叫方式不變,並且測試了執行效率
# 但是這裡還有個問題,如果func()裡面有引數的話,這樣就偽裝不徹底了
# 而且 func(f1) 上面還有兩行程式碼,如果要測試500個函式就要多寫至少1000行程式碼

 

 

# 裝飾器的雛形

import time
def func():
    print("測試這個函式的執行效率...")
    time.sleep(0.2)

def timmer(f):
    def inner():
        start_time = time.time()
        f()
        end_time = time.time()
        print("該函式的執行效率是%s" % (end_time - start_time))
    return inner

func = timmer(func)  # inner

# # 相當於 f = (func函式),但是不執行 inner 函式,因為沒有呼叫它(inner()),只是返回了 inner 這個函式名
# # 也就是(return innner),所以這裡相當於 inner 這個函式名賦值給一個變數 func
# func()  # inner()
# 這裡就是在呼叫 inner(),因為 inner() 中又有 f(), 因此還會呼叫最上面的 func() 函式

# 上面的timmer就是裝飾器
# 但是這裡還是多了一行程式碼,比如有另一個 func1(), 那麼還得再加一行:func1 = timmer(func1)
# Python直譯器提供了一個語法糖 @ 的概念

import time
def timmer(f):
    def inner():
        start_time = time.time()
        f()
        end_time = time.time()
        print("該函式的執行效率是%s" % (end_time - start_time))
    return inner

# # 前提是先定義一個 timmer 函式
@timmer  # 相當於 func =  timmer(func) ,要記住這裡的 timmer(func) 是 inner 函式
def func():
    print("測試這個函式的執行效率...")
    time.sleep(0.2)
func()

@timmer
def func1():
    print(666)
    time.sleep(0.2)
func1()

 

# 這還有個問題,被裝飾的函式 func、func1 如果有引數,這樣寫就滿足不了了
# 比如下面這樣

import time
def timmer(f):
    def inner():
        start_time = time.time()
        f()
        end_time = time.time()
        print("該函式的執行效率是%s" % (end_time - start_time))
    return inner

@timmer
def func(x):
    print("測試一個引數:%s" %x)
    time.sleep(0.2)
func("abc")  # 會報錯
# 因為這裡的 func("abc") 相當於 inner("abc"),而 inner() 是沒有引數的
# 因此要在 inner()中傳入一個引數

import time
def timmer(f):
    def inner(a):
        start_time = time.time()
        f(a)
        end_time = time.time()
        print("該函式的執行效率是%s" % (end_time - start_time))
    return inner

@timmer
def func(x):
    print("測試一個引數:%s" %x)
    time.sleep(0.2)
func("abc")

# 這個功能還是不完善,如果有兩個引數以上,inner() 及 inner 函式裡的 f() 裡面就得加入兩個引數
# 這時就要想到之前學過的動態引數(萬能引數)

 

import time
def timmer(f):
    def inner(*args, **kwargs):  # 函式的定義,* 代表聚合
        start_time = time.time()
        f(*args, **kwargs)
        # 比如這裡 func1 中的兩個萬能引數為什麼可以平均合理分配給 func 函式中 x, y, z 三個引數?
        # 因為 f(*args, **kwargs)是呼叫函式,而括號中的 *args **kwargs 就是打散
        # 所以這裡的 f(*args, **kwargs) 相當於 f("abc", "jjk", "hkas")
        end_time = time.time()
        print("該函式的執行效率是%s" % (end_time - start_time))
    return inner

@timmer
def func(x):
    print("測試一個引數:%s" %x)
    time.sleep(0.2)
func("abc")

@timmer
def func1(x, y, z="alex"):
    print("測試兩個引數:%s" % x, y)
    print("測試一個引數:%s" % z)
    time.sleep(0.2)
func("abc", "jjk", "hkas")

# 這裡還差一點,被裝飾函式缺少返回值

 

# 函式有返回值 return 時

import time
def timmer(f):
    def inner(*args, **kwargs):
        start_time = time.time()
        f(*args, **kwargs)
        end_time = time.time()
        print("該函式的執行效率是%s" % (end_time - start_time))
    return inner

@timmer
def func(x):
    print("測試一個引數:%s" %x)
    time.sleep(0.2)
    return 666
func("abc")
# 執行發現沒有返回 666,因為 inner() 中沒有返回值

import time
def timmer(f):
    def inner(*args, **kwargs):
        start_time = time.time()
        ret = f(*args, **kwargs)  # 注意這裡
        end_time = time.time()
        print("該函式的執行效率是%s" % (end_time - start_time))
        return ret  # 藉助 inner() 函式設定一個返回值
    return inner

@timmer
def func(x):
    print("測試一個引數:%s" %x)
    time.sleep(0.2)
    return 666
s = func("abc")
print(s)

# 這裡發現去掉 @timmer 也一樣能打印出 666

 

# 標準版裝飾器寫法

def wrapper(f):
    def inner(*args, **kwargs):
        """執行被裝飾函式之前的操作"""
        ret = f(*args, **kwargs)
        """執行被裝飾函式之後的操作"""
        return ret
    return inner

# 裝飾器:
#   1. 它的本質是閉包,而且它就是個函式
#   2. 在不改變原函式呼叫方式上,給原函式增加了一些額外的功能,比如登入驗證,寫日誌,執行效率等

# 開放封閉原則:
#   對程式碼拓展開放——任何一個程式,不可能在設計之初就已經想好了所有的功能並且未來不做任何更新和修改。
#                  所以我們必須允許程式碼擴充套件、新增新功能
#   對程式碼修改封閉——因為寫的函式,很有可能已經交付給其他人使用了,如果這個時候我們對其進行了修改,
#                  很有可能影響其他已經在使用該函式的使用者。

# 而裝飾器完美的遵循了這個開放封閉原則。

# 比如上一段程式碼中的 func() 函式如果還要加一個功能
# 不要在這個被裝飾函式裡改,只需在裝飾器 timmer 裡的 inner 函式新增即可