python_裝飾器

#函數(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)功能

  1. 引入日志
  2. 函數(shù)執(zhí)行時(shí)間統(tǒng)計(jì)
  3. 執(zhí)行函數(shù)前預(yù)備處理
  4. 執(zhí)行函數(shù)后清理功能
  5. 權(quán)限校驗(yàn)等場(chǎng)景
  6. 緩存
#無(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---
"""

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 部分細(xì)節(jié)自己改了點(diǎn),也加了點(diǎn)自己例子,基本上屬于轉(zhuǎn)載。轉(zhuǎn)載出處:https://my.oschina.net/le...
    洛克黃瓜閱讀 2,114評(píng)論 0 3
  • 很多寫裝飾器的都是直接甩給你最終的裝飾器代碼,然后給你說(shuō)下大致的原理,比如: 然后來(lái)上一句,把@log放到now(...
    ztfdeveloper閱讀 347評(píng)論 0 0
  • 每個(gè)人都有的內(nèi)褲主要功能是用來(lái)遮羞,但是到了冬天它沒法為我們防風(fēng)御寒,咋辦?我們想到的一個(gè)辦法就是把內(nèi)褲改造一下,...
    chen_000閱讀 1,403評(píng)論 0 3
  • 裝飾器本質(zhì)上是一個(gè)Python函數(shù),它可以讓其他函數(shù)在不需要做任何代碼變動(dòng)的前提下增加額外功能,裝飾器的返回值也是...
    編程自習(xí)室閱讀 230評(píng)論 0 1
  • Python 中的函數(shù)和 Java、C++不太一樣,Python 中的函數(shù)可以像普通變量一樣當(dāng)做參數(shù)傳遞給另外一個(gè)...
    stoneyang94閱讀 1,429評(píng)論 1 4

友情鏈接更多精彩內(nèi)容