#函數(shù)引用, 對(duì)于函數(shù)式編程來(lái)說(shuō),函數(shù)也是變量,就存在函數(shù)引用
#閉包
def line_conf(a, b):
# 在函數(shù)內(nèi)部再定義一個(gè)函數(shù),并且這個(gè)函數(shù)用到了外邊函數(shù)的變量,那么將這個(gè)函數(shù)以及用到的一些變量稱之為閉包
def line(x):
# 其實(shí)這里返回的就是閉包的結(jié)果
print("a:{}, b:{}, x:{}".format(a,b,x))
print("a*x+b={}".format(a*x + b))
return line
line1 = line_conf(1, 1)
line2 = line_conf(4, 5)
line1(5)
line2(5)
#運(yùn)行結(jié)果
a:1, b:1, x:5
a*x+b=6
a:4, b:5, x:5
a*x+b=25
#這個(gè)例子中,函數(shù)line與變量a,b構(gòu)成閉包。在創(chuàng)建閉包的時(shí)候,我們通過line_conf的參數(shù)a,b說(shuō)明了這兩個(gè)變量的取值,這樣,我們就確定了函數(shù)的最終形式(y = x + 1和y = 4x + 5)。我們只需要變換參數(shù)a,b,就可以獲得不同的直線表達(dá)函數(shù)。由此,我們可以看到,閉包也具有提高代碼可復(fù)用性的作用。
#如果沒有閉包,我們需要每次創(chuàng)建直線函數(shù)的時(shí)候同時(shí)說(shuō)明a,b,x。這樣,我們就需要更多的參數(shù)傳遞,也減少了代碼的可移植性。
#注意點(diǎn): 由于閉包引用了外部函數(shù)的局部變量,則外部函數(shù)的局部變量沒有及時(shí)釋放,消耗內(nèi)存
#裝飾器準(zhǔn)備知識(shí)
#### 第一波 ####
def foo():
print('foo')
foo # 表示是函數(shù)
foo() # 表示執(zhí)行foo函數(shù)
#### 第二波 ####
def foo():
print('foo')
foo = lambda x: x + 1
foo() # 執(zhí)行l(wèi)ambda表達(dá)式,而不再是原來(lái)的foo函數(shù),因?yàn)閒oo這個(gè)名字被重新指向了另外一個(gè)匿名函數(shù)
#結(jié)論:
#函數(shù)名僅僅是個(gè)變量,只不過指向了定義的函數(shù)而已,所以才能通過 函數(shù)名()調(diào)用,如果 函數(shù)名=xxx被修改了,那么當(dāng)在執(zhí)行 函數(shù)名()時(shí),調(diào)用的就不知之前的那個(gè)函數(shù)了
裝飾器(decorator)功能
- 引入日志
- 函數(shù)執(zhí)行時(shí)間統(tǒng)計(jì)
- 執(zhí)行函數(shù)前預(yù)備處理
- 執(zhí)行函數(shù)后清理功能
- 權(quán)限校驗(yàn)等場(chǎng)景
- 緩存
#無(wú)參數(shù)的函數(shù)
from time import ctime, sleep
def timefun(func):
def wrapped_func():
print("%s called at %s" % (func.__name__, ctime()))
func()
return wrapped_func
@timefun
def foo():
print("I am foo")
foo()
sleep(2)
foo()
"""
#上面代碼理解裝飾器執(zhí)行行為可理解成
foo = timefun(foo)
# foo先作為參數(shù)賦值給func后,foo接收指向timefun返回的wrapped_func
foo()
# 調(diào)用foo(),即等價(jià)調(diào)用wrapped_func()
# 內(nèi)部函數(shù)wrapped_func被引用,所以外部函數(shù)的func變量(自由變量)并沒有釋放
# func里保存的是原foo函數(shù)對(duì)象
"""
#被裝飾的函數(shù)有參數(shù)
from time import ctime, sleep
def timefun(func):
def wrapped_func(a, b):
print("%s called at %s" % (func.__name__, ctime()))
print(a, b)
func(a, b)
return wrapped_func
@timefun
def foo(a, b):
print(a+b)
foo(3,5)
sleep(2)
foo(2,4)
#被裝飾的函數(shù)有不定長(zhǎng)參數(shù)
from time import ctime, sleep
def timefun(func):
def wrapped_func(*args, **kwargs):
print("%s called at %s"%(func.__name__, ctime()))
func(*args, **kwargs)
return wrapped_func
@timefun
def foo(a, b, c):
print(a+b+c)
foo(3,5,7)
sleep(2)
foo(2,4,9)
#裝飾器帶參數(shù),在原有裝飾器的基礎(chǔ)上,設(shè)置外部變量
from time import ctime, sleep
def timefun_arg(pre="hello"):
def timefun(func):
def wrapped_func():
print("%s called at %s %s" % (func.__name__, ctime(), pre))
return func()
return wrapped_func
return timefun
# 下面的裝飾過程
# 1. 調(diào)用timefun_arg("itcast")
# 2. 將步驟1得到的返回值,即time_fun返回, 然后time_fun(foo)
# 3. 將time_fun(foo)的結(jié)果返回,即wrapped_func
# 4. 讓foo = wrapped_fun,即foo現(xiàn)在指向wrapped_func
@timefun_arg("itcast")
def foo():
print("I am foo")
@timefun_arg("python")
def too():
print("I am too")
foo()
sleep(2)
foo()
too()
sleep(2)
too()
#可以理解為 foo()==timefun_arg("itcast")(foo)()
#裝飾器函數(shù)其實(shí)是這樣一個(gè)接口約束,它必須接受一個(gè)callable對(duì)象作為參數(shù),然后返回一個(gè)callable對(duì)象。在Python中一般callable對(duì)象都是函數(shù),但也有例外。只要某個(gè)對(duì)象重寫了 __call__() 方法,那么這個(gè)對(duì)象就是callable的
class Test():
def __call__(self):
print('call me!')
t = Test()
t() # call me
#類裝飾器demo
class Test(object):
def __init__(self, func):
print("---初始化---")
print("func name is %s"%func.__name__)
self.__func = func
def __call__(self):
print("---裝飾器中的功能---")
self.__func()
@Test
def test():
print("----test---")
test()
showpy()#如果把這句話注釋,重新運(yùn)行程序,依然會(huì)看到"--初始化--"
"""
#說(shuō)明:
#1. 當(dāng)用Test來(lái)裝作裝飾器對(duì)test函數(shù)進(jìn)行裝飾的時(shí)候,首先會(huì)創(chuàng)建Test的實(shí)例對(duì)象
# 并且會(huì)把test這個(gè)函數(shù)名當(dāng)做參數(shù)傳遞到__init__方法中
# 即在__init__方法中的屬性__func指向了test指向的函數(shù)
#
#2. test指向了用Test創(chuàng)建出來(lái)的實(shí)例對(duì)象
#
#3. 當(dāng)在使用test()進(jìn)行調(diào)用時(shí),就相當(dāng)于讓這個(gè)對(duì)象(),因此會(huì)調(diào)用這個(gè)對(duì)象的__call__方法
#
#4. 為了能夠在__call__方法中調(diào)用原來(lái)test指向的函數(shù)體,所以在__init__方法中就需要一個(gè)實(shí)例屬性來(lái)保存這個(gè)函數(shù)體的引用
# 所以才有了self.__func = func這句代碼,從而在調(diào)用__call__方法中能夠調(diào)用到test之前的函數(shù)體
運(yùn)行結(jié)果如下:
---初始化---
func name is test
---裝飾器中的功能---
----test---
"""