Python高階 -- 11 閉包、裝飾器
阿新 • • 發佈:2019-02-03
一、閉包
1、什麼是閉包
# 定義一個函式 def test(number): """ 在函式內部再定義一個函式,並且這個函式用到了外邊函式的變數, 同時,外部函式的返回值是內部函式的引用 那麼將這個函式(內部定義的那個函式)以及用到的一些變數稱之為閉包 """ def test_in(number_in): print("in test_in 函式, number_in is %d" % number_in) return number + number_in # 其實這裡返回的就是閉包的結果 return test_in # 給test函式賦值,這個20就是給引數number ret = test(20) # 注意這裡的100其實是給test函式內部定義的test_in函式的引數number_in賦值 # ret是test函式的返回值,即返回的是test函式內部的定義的test_in函式的引用 print(ret(100)) #注 意這裡的200其實給引數number_in print(ret(200))
二、裝飾器
1、先理解下面程式碼
#### 第一波 ####
def foo():
print('foo')
foo # 表示是函式的引用
foo() # 表示執行foo函式
#### 第二波 ####
def foo():
print('foo')
foo = lambda x: x + 1
foo() # 執行lambda表示式,而不再是原來的foo函式,因為foo這個名字被重新指向了另外一個匿名函式
# 以上程式碼是將lambda表示式賦值給了foo變數,此時,執行foo(),代表的是執行lambda表示式
函式名僅僅是個變數,只不過指向了定義的函式而已,所以才能通過 函式名()呼叫;如果 變數被修改了,即函式名=xxx ,那麼當在執行 函式名() 時,呼叫的就不是之前的那個函數了,而是被賦值之後的函式
2、裝飾器
使用閉包引入裝飾器概念
def validation(fun): # 定義一個內部函式 def doValidation(): # 執行前置校驗 print("放在 fun() 之前的內容是屬於前置校驗") # 執行fun函式 fun() # 執行後置校驗 print("放在 fun() 之後的內容是屬於後置校驗") # 外部函式返回值是內部函式的引用 return doValidation def test(): print("test方法中加入校驗") """ 例項化外部函式,引數是其他函式(test函式)的引用 由於外部函式(validation)中還有一個函式定義, 因此,例項化外部函式的時候,會定義一個內部函式,返回的是內部函式的引用 而由於,在內部函式中使用其他函式(test函式)的引用來例項化了一個其他函式(test函式)物件, 因此,例項化內部函式的時候,會進行其他函式(test函式)的例項化操作 """ test = validation(test) # 此時的test是內部函式的引用 """ 執行內部函式,由於內部函式中其他函式(test)的例項化操作 因此,在內部函式中所執行的程式碼(本例中為列印操作)可以放在其他函式(test)前或者後進行操作 即:達到了在執行其他函式前(或者後)進行校驗的目的 """ test() """ 執行結果: 放在 fun() 之前的內容是屬於前置校驗 test方法中加入校驗 放在 fun() 之後的內容是屬於後置校驗 """
使用裝飾器來進行上述操作的程式碼:
def validation(fun):
# 定義一個內部函式
def doValidation():
# 執行前置校驗
print("放在 fun() 之前的內容是屬於前置校驗")
# 執行fun函式
fun()
# 執行後置校驗
print("放在 fun() 之後的內容是屬於後置校驗")
# 外部函式返回值是內部函式的引用
return doValidation
@validation
def test():
print("test方法中加入校驗")
"""
例項化外部函式,引數是其他函式(test函式)的引用
由於外部函式(validation)中還有一個函式定義,
因此,例項化外部函式的時候,會定義一個內部函式,返回的是內部函式的引用
而由於,在內部函式中使用其他函式(test函式)的引用來例項化了一個其他函式(test函式)物件,
因此,例項化內部函式的時候,會進行其他函式(test函式)的例項化操作
⭐⭐⭐在其他函式上使用 @外部函式名 ,這種方式可以替代以下程式碼來進行test方法執行前後的校驗
test = validation(test) # 返回值是內部函式的引用
"""
"""
執行內部函式,由於內部函式中其他函式(test)的例項化操作
因此,在內部函式中所執行的程式碼(本例中為列印操作)可以放在其他函式(test)前或者後進行操作
即:達到了在執行其他函式前(或者後)進行校驗的目的
"""
test()
"""
執行結果:
放在 fun() 之前的內容是屬於前置校驗
test方法中加入校驗
放在 fun() 之後的內容是屬於後置校驗
"""
解釋:在方法上加上 @其他方法名 的方式即為裝飾器的使用,而其他方法是一個包含了內部函式的一個函式,在內部函式中有方法的例項化操作,這種方式就達到了裝飾器的效果
3、對帶有引數的方法進行裝飾器
不使用裝飾器的寫法:
def validation(fun):
# 定義一個內部函式
def doValidation(args):
# 執行前置校驗
print("放在 fun() 之前的內容是屬於前置校驗")
# 執行fun函式
fun(args)
# 執行後置校驗
print("放在 fun() 之後的內容是屬於後置校驗")
# 外部函式返回值是內部函式的引用
return doValidation
def test(args):
print("test方法中加入校驗 %s" % args)
test = validation(test) # 返回值是是內部函式的引用
test(100)
使用裝飾器的寫法:
def validation(fun):
# 定義一個內部函式
def doValidation(args):
# 執行前置校驗
print("放在 fun() 之前的內容是屬於前置校驗")
# 執行fun函式
fun(args)
# 執行後置校驗
print("放在 fun() 之後的內容是屬於後置校驗")
# 外部函式返回值是內部函式的引用
return doValidation
@validation
def test(args):
print("test方法中加入校驗 %s" % args)
# test = validation(test) # 返回值是是內部函式的引用
test(100)
4、不定長引數的函式裝飾器
def validation(fun):
# 定義一個內部函式
def doValidation(*args, **kwargs):
# 執行前置校驗
print("放在 fun() 之前的內容是屬於前置校驗")
# 執行fun函式
fun(*args, **kwargs)
# 執行後置校驗
print("放在 fun() 之後的內容是屬於後置校驗")
# 外部函式返回值是內部函式的引用
return doValidation
@validation # test = validation(test) 返回值是是內部函式的引用
def test(num, *args, **kwargs):
print("test方法中引數1 %s" % num)
print("test方法中引數2 : " , args)
print("test方法中引數3 : " , kwargs)
test(100)
print("-" * 50)
test(100, 200)
print("-" * 50)
test(100, 200, 300, name='李四')
"""
執行結果:
放在 fun() 之前的內容是屬於前置校驗
test方法中引數1 100
test方法中引數2 : ()
test方法中引數3 : {}
放在 fun() 之後的內容是屬於後置校驗
--------------------------------------------------
放在 fun() 之前的內容是屬於前置校驗
test方法中引數1 100
test方法中引數2 : (200,)
test方法中引數3 : {}
放在 fun() 之後的內容是屬於後置校驗
--------------------------------------------------
放在 fun() 之前的內容是屬於前置校驗
test方法中引數1 100
test方法中引數2 : (200, 300)
test方法中引數3 : {'name': '李四'}
放在 fun() 之後的內容是屬於後置校驗
"""
5、帶有返回值的函式的裝飾器
帶有返回值的函式的裝飾器亦是通用裝飾器(可變引數,有返回值的裝飾器)的寫法:
def validation(fun):
# 定義一個內部函式
def doValidation(*args, **kwargs):
# 執行前置校驗
print("放在 fun() 之前的內容是屬於前置校驗")
# 執行fun函式
return fun(*args, **kwargs)
# 執行後置校驗
print("放在 fun() 之後的內容是屬於後置校驗")
# 外部函式返回值是內部函式的引用
return doValidation
@validation # test = validation(test) 返回值是是內部函式的引用
def test(num, *args, **kwargs):
print("test方法中引數1 %s" % num)
print("test方法中引數2 : " , args)
print("test方法中引數3 : " , kwargs)
return "OK"
ret = test(100)
print(ret)
print("-" * 50)
ret = test(100, 200)
print(ret)
print("-" * 50)
ret = test(100, 200, 300, name='李四')
print(ret)
"""
執行結果:
放在 fun() 之前的內容是屬於前置校驗
test方法中引數1 100
test方法中引數2 : ()
test方法中引數3 : {}
OK
--------------------------------------------------
放在 fun() 之前的內容是屬於前置校驗
test方法中引數1 100
test方法中引數2 : (200,)
test方法中引數3 : {}
OK
--------------------------------------------------
放在 fun() 之前的內容是屬於前置校驗
test方法中引數1 100
test方法中引數2 : (200, 300)
test方法中引數3 : {'name': '李四'}
OK
"""
6、多個裝飾器對一個函式進行裝飾
def zhuangshi_1(fun):
print("定義裝飾1")
# 定義一個內部函式
def neibu_1(*args, **kwargs):
# 執行前置校驗
print("執行 zhuangshi_1 中的裝飾內容")
# 執行fun函式
return fun(*args, **kwargs)
# 外部函式返回值是內部函式的引用
return neibu_1
def zhuangshi_2(fun):
print("定義裝飾2")
# 定義一個內部函式
def neibu_2(*args, **kwargs):
# 執行前置校驗
print("執行 zhuangshi_2 中的裝飾內容")
# 執行fun函式
return fun(*args, **kwargs)
# 外部函式返回值是內部函式的引用
return neibu_2
@zhuangshi_1
@zhuangshi_2
def test():
pass
test()
"""
執行結果:
定義裝飾2
定義裝飾1
執行 zhuangshi_1 中的裝飾內容
執行 zhuangshi_2 中的裝飾內容
解釋:
定義裝飾器的時候,先執行離方法最近的裝飾器的定義
執行裝飾器的時候,先執行最上層的裝飾器
"""
7、多個裝飾器裝飾一個函式的應用
def html_1(func):
def td():
return "<td>" + func() +"</td>"
return td
def html_2(func):
def p1():
return "<p1>" + func() + "</p1>"
return p1
@html_1
@html_2
def test():
return "hello world"
print(test())
"""
執行結果:
<td><p1>hello world</p1></td>
"""
8、帶有引數的裝飾器
def parent(arg):
def validation(func):
def call_func(*args, **kwargs):
if arg == 1:
print("level 1")
elif arg == 2:
print("level 2")
return func()
return call_func
return validation
"""
⭐⭐⭐:帶有引數的裝飾器呼叫的時候:
首先會將引數當作實參,傳遞到裝飾器中,進行第一層函式的呼叫,並返回第二層函式的引用;
然後,將第二層函式的引用當作真正的裝飾器,進行裝飾
以下呼叫中,首先@parent(1)會先執行parent方法,把引數1傳遞進去,返回的是validation的引用
然後,使用validation作為真正的裝飾器,對test1方法進行裝飾
"""
@parent(1)
def test1():
return "OK"
test1()
@parent(2)
def test2():
return "OK"
test2()
9、用類對函式進行裝飾
class Test(object):
def __init__(self, func):
self.func = func
def __call__(self):
print("對函式進行裝飾的方法")
return self.func()
@Test
def demo():
return "呵呵"
print(demo())