1. 閉包
在函數內部再定義一個函數,并且內部函數用到了外部函數作用域里的變量(enclosing)
,那么將這個內部函數以及用到的外部函數內的變量一起稱為閉包(Closure)
基本格式:
def 外部函數名():
內部函數需要的變量
def 內部函數名()
引用外部的變量
return 內部函數
- 注意:
函數的作用域關系在函數定義階段就已經固定,與調用位置無關。
無論函數在何處調用,都需要回到定義階段去找對應的作用域關系。
閉包中不要引用外部函數中任何循環變量或后續會發生變化的變量(延遲計算)
2. 裝飾器
2.1 裝飾器語法糖
@
符號就是裝飾器的語法糖
它放在一個函數開始定義的地方,它就像一頂帽子一樣戴在這個函數的頭上。和這個函數綁定在一起。在我們調用這個函數的時候,第一件事并不是執行這個函數,而是將這個函數做為參數傳入它頭頂上這頂帽子,這頂帽子我們稱之為裝飾函數
或 裝飾器
裝飾器的使用方法很固定:
(1) 先定義一個裝飾函數(帽子)(也可以用類、偏函數實現)
(2) 再定義你的業務函數、或者類(人)
(3) 最后把這頂帽子帶在這個人頭上
2.2 裝飾器無參數
格式:
def 裝飾器外部函數(func): #func 就是被裝飾函數
def裝飾器內部函數(*args, **kwargs): #裝飾器內部函數是一個閉包,它使用了外部函數的變量func裝飾函數的功能
裝飾器的功能
return func(*args, **kwargs) #執行被裝飾函數并返回被裝飾函數的返回值
return 返回裝飾器內部函數名 #注!返回裝飾器內部函數名而不是裝飾器內部函數
@裝飾器外部函數名 #將被裝飾函數傳入裝飾器,返回裝飾器內部函數引用
def 被裝飾函數(被裝飾函數的參數):
被裝飾函數的功能
return 被裝飾函數的返回值
接收返回值 = 被裝飾函數(被裝飾函數的參數) #執行函數,即執行裝飾器內部函數,這個閉包使用的func變量指向被裝飾函數體
print(接收返回值) #打印一下接收的返回值
2.4 裝飾器帶參數
根據功能需求,在原來的裝飾器外面再加一層函數
格式:
def 需求函數(需求函數參數):
def 裝飾器外部函數(func):
def 裝飾器內部函數(*args, **kwargs):
裝飾器的功能
return func(*args, **kwargs)
return 裝飾器內部函數
return 裝飾器外部函數
@需求函數('需求函數參數') # 等價于 被裝飾函數 = 需求函數('需求函數參數')(被裝飾函數) ,即先執行需求函數('需求函數參數'),返回裝飾
#(接上一行的解釋)器外部函數引用(真正的裝飾器),再用裝飾器外部函數裝飾被裝飾函數,返回裝飾器內部函數
def 被裝飾函數(被裝飾函數的參數):
被裝飾函數的功能
return 被裝飾函數的返回值
接收返回值 = 被裝飾函數(被裝飾函數的參數) #執行函數,即執行裝飾器內部函數,這個閉包使用的func變量指向被裝飾函數體
print(接收返回值) #打印一下接收的返回值
2.5 使用@wraps
當打印被裝飾函數函數名時,因為被裝飾函數是在裝飾器內部函數調用所以顯示裝飾器內部函數名,想打印被裝飾函數名時可以使用functools
模塊的wraps
裝飾器解決這個問題
格式:
from functools import wraps
def 需求函數(需求函數參數):
def 裝飾器外部函數(func):
@wraps(func)
def 裝飾器內部函數(*args, **kwargs):
裝飾器的功能
return func(*args, **kwargs)
return 裝飾器內部函數
return 裝飾器外部函數
@需求函數('需求函數參數') # 等價于 被裝飾函數 = 需求函數('需求函數參數')(被裝飾函數) ,即先執行需求函數('需求函數參數'),返回裝飾
#(接上一行解釋)器外部函數引用(真正的裝飾器),再用裝飾器外部函數裝飾被裝飾函數,返回裝飾器內部函數
def 被裝飾函數(被裝飾函數的參數):
被裝飾函數的功能
return 被裝飾函數的返回值
接收返回值 = 被裝飾函數(被裝飾函數的參數) #執行函數,即執行裝飾器內部函數,這個閉包使用的func變量指向被裝飾函數體
print(接收返回值) #打印一下接收的返回值
2.6 多個裝飾器裝飾同一個函數
格式:
# 裝飾器1
def 裝飾器外部函數(func): #func 就是被裝飾函數
def裝飾器內部函數(*args, **kwargs): #裝飾器內部函數是一個閉包,它使用了外部函數的變量func裝飾函數的功能
裝飾器的功能
return func(*args, **kwargs) #執行被裝飾函數并返回被裝飾函數的返回值
return 返回裝飾器內部函數名 #注!返回裝飾器內部函數名而不是裝飾器內部函數
# 裝飾器2
def 裝飾器外部函數(func): #func 就是被裝飾函數
def裝飾器內部函數(*args, **kwargs): #裝飾器內部函數是一個閉包,它使用了外部函數的變量func裝飾函數的功能
裝飾器的功能
return func(*args, **kwargs) #執行被裝飾函數并返回被裝飾函數的返回值
return 返回裝飾器內部函數名 #注!返回裝飾器內部函數名而不是裝飾器內部函數
@裝飾器1的外部函數名 #將被裝飾函數傳入裝飾器,返回裝飾器內部函數引用
@裝飾器2的外部函數名 #將被裝飾函數傳入裝飾器,返回裝飾器內部函數引用
def 被裝飾函數(被裝飾函數的參數):
被裝飾函數的功能
return 被裝飾函數的返回值
接收返回值 = 被裝飾函數(被裝飾函數的參數) #執行函數,即執行裝飾器內部函數,這個閉包使用的func變量指向被裝飾函數體
print(接收返回值) #打印一下接收的返回值
小結:多個裝飾器的順序,包裝時,是從下往上的,也就是被裝飾函數頭上,哪個離得近就先包裝哪個
2.7 基于類實現的裝飾器
基于類裝飾器的實現,必須實現__call__
和 __init__
兩個內置函數。
2.7.1 不帶參數的類裝飾器
-
__init__
:接收被裝飾函數 -
__call__
:實現裝飾邏輯
格式:
class 類裝飾器函數(object):
def __init__(self, func): #接收被裝飾函數
self.func = func
def __call__(self, *args, **kwargs): #實現裝飾邏輯
裝飾器函數的功能
return self.func(*args, **kwargs)
@類裝飾器函數名
def 被裝飾函數(被裝飾函數的參數):
被裝飾函數的功能
被裝飾函數(被裝飾函數的參數) #執行函數
2.7.2 帶參數的類裝飾器
帶參數和不帶參數的類裝飾器有很大的不同。
__init__
:不再接收被裝飾函數,而是接收傳入參數。
__call__
:接收被裝飾函數,實現裝飾邏輯。
格式:
class 類裝飾器(object):
def __init__(self, level='類裝飾器函數參數'):
self.level = level
def __call__(self, func): # 接受函數
def 裝飾器內部函數(*args, **kwargs):
裝飾器的功能
func(*args, **kwargs)
return 裝飾器內部函數名 #返回函數
@類裝飾器函數名(level='類裝飾器函數參數')
def 被裝飾函數(被裝飾函數的參數):
被裝飾函數的功能
被裝飾函數(被裝飾函數的參數) #執行函數