久久久久久久视色,久久电影免费精品,中文亚洲欧美乱码在线观看,在线免费播放AV片

<center id="vfaef"><input id="vfaef"><table id="vfaef"></table></input></center>

    <p id="vfaef"><kbd id="vfaef"></kbd></p>

    
    
    <pre id="vfaef"><u id="vfaef"></u></pre>

      <thead id="vfaef"><input id="vfaef"></input></thead>

    1. 站長(zhǎng)資訊網(wǎng)
      最全最豐富的資訊網(wǎng)站

      歸納總結(jié)Python中的裝飾器知識(shí)點(diǎn)

      本篇文章給大家?guī)砹岁P(guān)于python的相關(guān)知識(shí),其中主要介紹了關(guān)于裝飾器的相關(guān)問題,包括了閉包、裝飾器、使用多個(gè)裝飾器、帶參數(shù)的裝飾器等等內(nèi)容,下面一起來看一下,希望對(duì)大家有幫助。

      歸納總結(jié)Python中的裝飾器知識(shí)點(diǎn)

      推薦學(xué)習(xí):python視頻教程

      一、閉包

      要了解什么是裝飾器(decorator),我們首先需要知道閉包(closure)的概念。

      閉包,又稱閉包函數(shù)或者閉合函數(shù),通俗一點(diǎn)來講,當(dāng)某個(gè)函數(shù)被當(dāng)成對(duì)象返回時(shí)還夾帶了外部變量,就形成了一個(gè)閉包。

      以打印Hello World為例,我們先來看一下嵌套函數(shù)的結(jié)構(gòu)應(yīng)該是什么樣的:

      def print_msg(msg):      def printer():         print(msg)      printer()print_msg('Hello World')# Hello World

      執(zhí)行 print_msg('Hello World') 相當(dāng)于執(zhí)行了 printer(),也就是執(zhí)行 print(msg),所以將輸出 Hello World。

      我們?cè)賮砜匆幌氯绻情]包,該是什么樣的結(jié)構(gòu):

      def print_msg(msg):      def printer():         print(msg)      return printer   my_msg = print_msg('Hello World')my_msg()# Hello World

      本例中的 printer 函數(shù)就是閉包。

      執(zhí)行 print_msg('Hello World') 實(shí)際上是返回了如下這樣一個(gè)函數(shù),它夾帶了外部變量 'Hello World'

      def printer():     print('Hello World')

      于是調(diào)用 my_msg 就相當(dāng)于執(zhí)行 printer()


      那么如何判斷一個(gè)函數(shù)是否是閉包函數(shù)呢?閉包函數(shù)的 __closure__ 屬性里面定義了一個(gè)元組用于存放所有的cell對(duì)象,每個(gè)cell對(duì)象保存了這個(gè)閉包中所有的外部變量。而普通函數(shù)的 __closure__ 屬性為 None。

      def outer(content):      def inner():         print(content)      return innerprint(outer.__closure__)     # Noneinner = outer('Hello World')print(inner.__closure__)     # (<cell at 0x0000023FB1FD0B80: str object at 0x0000023FB1DC84F0>,)

      由此可見 outer 函數(shù)不是閉包,而 inner 函數(shù)是閉包。

      我們還可以查看閉包所攜帶的外部變量:

      print(inner.__closure__[0].cell_contents)# Hello World

      說了那么多,那么閉包究竟有什么用呢?閉包存在的意義就是它夾帶了外部變量(私貨),如果它不夾帶私貨,那么就和普通的函數(shù)沒有任何區(qū)別。

      閉包的優(yōu)點(diǎn)如下:

      • 局部變量無法共享和長(zhǎng)久的保存,而全局變量可能造成變量污染,閉包既可以長(zhǎng)久的保存變量又不會(huì)造成全局污染。
      • 閉包使得函數(shù)內(nèi)局部變量的值始終保持在內(nèi)存中,不會(huì)在外部函數(shù)調(diào)用后被自動(dòng)清除。

      二、裝飾器

      我們先考慮這樣一個(gè)場(chǎng)景,假設(shè)先前編寫的一個(gè)函數(shù)已經(jīng)實(shí)現(xiàn)了4個(gè)功能,為簡(jiǎn)便起見,我們用 print 語句來代表每一個(gè)具體的功能:

      def module():     print('功能1')     print('功能2')     print('功能3')     print('功能4')

      現(xiàn)在,由于某種原因,你需要為 module 這個(gè)函數(shù)新增一個(gè) 功能5,你完全可以這樣修改:

      def module():     print('功能1')     print('功能2')     print('功能3')     print('功能4')     print('功能5')

      但在現(xiàn)實(shí)業(yè)務(wù)中,直接做出這樣的修改往往是比較危險(xiǎn)的(會(huì)變得不易于維護(hù))。那么如何在不修改原函數(shù)的基礎(chǔ)上去為它新添一個(gè)功能呢?

      你可能已經(jīng)想到了使用之前的閉包知識(shí):

      def func_5(original_module):      def wrapper():         original_module()         print('功能5')      return wrapper

      func_5 代表該函數(shù)主要用于實(shí)現(xiàn) 功能5,我們接下來將 module 傳入進(jìn)去來觀察效果:

      new_module = func_5(module)new_module()# 功能1# 功能2# 功能3# 功能4# 功能5

      可以看出,我們的新模塊:new_module 已經(jīng)實(shí)現(xiàn)了 功能5。

      在上面的例子中,函數(shù) func_5 就是一個(gè)裝飾器,它裝飾了原來的模塊(為它新添了一個(gè)功能)。

      當(dāng)然,Python有更簡(jiǎn)潔的寫法(稱之為語法糖),我們可以將@符號(hào)與裝飾器函數(shù)的名稱一起使用,并將其放置在要裝飾的函數(shù)的定義上方:

      def func_5(original_module):      def wrapper():         original_module()         print('功能5')      return wrapper@func_5def module():     print('功能1')     print('功能2')     print('功能3')     print('功能4')module()# 功能1# 功能2# 功能3# 功能4# 功能5

      基于此,我們可以在不修改原函數(shù)的基礎(chǔ)上完成計(jì)時(shí)任務(wù)(計(jì)算原函數(shù)的運(yùn)行時(shí)間),如下:

      def timer(func):      def wrapper():         import time         tic = time.time()         func()         toc = time.time()         print('程序用時(shí): {}s'.format(toc - tic))      return wrapper@timerdef make_list():     return [i * i for i in range(10**7)]my_list = make_list()# 程序用時(shí): 0.8369960784912109s

      事實(shí)上,my_list 并不是列表,直接打印會(huì)顯示 None,這是因?yàn)槲覀兊?wrapper 函數(shù)沒有設(shè)置返回值。如果需要獲得 make_list 的返回值,可以這樣修改 wrapper 函數(shù):

      def wrapper():     import time     tic = time.time()     a = func()     toc = time.time()     print('程序用時(shí): {}s'.format(toc - tic))     return a

      三、使用多個(gè)裝飾器

      假如我們要為 module 新添 功能5功能6(按數(shù)字順序),那該如何做呢?

      好在Python允許同時(shí)使用多個(gè)裝飾器:

      def func_5(original_module):     def wrapper():         original_module()         print('功能5')     return wrapperdef func_6(original_module):     def wrapper():         original_module()         print('功能6')     return wrapper@func_6@func_5def module():     print('功能1')     print('功能2')     print('功能3')     print('功能4')module()# 功能1# 功能2# 功能3# 功能4# 功能5# 功能6

      上述過程實(shí)際上等價(jià)于:

      def module():     print('功能1')     print('功能2')     print('功能3')     print('功能4')new_module = func_6(func_5(module))new_module()

      此外,需要注意的是,在使用多個(gè)裝飾器時(shí),最靠近函數(shù)定義的裝飾器會(huì)最先裝飾該函數(shù),如果我們改變裝飾順序,則輸出結(jié)果也將改變:

      @func_5@func_6def module():     print('功能1')     print('功能2')     print('功能3')     print('功能4')module()# 功能1# 功能2# 功能3# 功能4# 功能6# 功能5

      四、被裝飾的函數(shù)帶有參數(shù)

      如果被裝飾的函數(shù)帶有參數(shù),那該如何去構(gòu)造裝飾器呢?

      考慮這樣一個(gè)函數(shù):

      def pide(a, b):     return a / b

      當(dāng)b=0 時(shí)會(huì)出現(xiàn) ZeropisionError。如何在避免修改該函數(shù)的基礎(chǔ)上給出一個(gè)更加人性化的提醒呢?

      因?yàn)槲覀兊?pide 函數(shù)接收兩個(gè)參數(shù),所以我們的 wrapper 函數(shù)也應(yīng)當(dāng)接收兩個(gè)參數(shù):

      def smart_pide(func):     def wrapper(a, b):         if b == 0:             return '被除數(shù)不能為0!'         else:             return func(a, b)     return wrapper

      使用該裝飾器進(jìn)行裝飾:

      @smart_pidedef pide(a, b):     return a / bprint(pide(3, 0))# 被除數(shù)不能為0!print(pide(3, 1))# 3.0

      如果不知道要被裝飾的函數(shù)有多少個(gè)參數(shù),我們可以使用下面更為通用的模板:

      def decorator(func):     def wrapper(*args, **kwargs):         # ...         res = func(*args, **kwargs)         # ...         return res  # 也可以不return     return wrapper

      五、帶參數(shù)的裝飾器

      我們之前提到的裝飾器都沒有帶參數(shù),即語法糖 @decorator 中沒有參數(shù),那么該如何寫一個(gè)帶參數(shù)的裝飾器呢?

      前面實(shí)現(xiàn)的裝飾器都是兩層嵌套函數(shù),而帶參數(shù)的裝飾器是一個(gè)三層嵌套函數(shù)。

      考慮這樣一個(gè)場(chǎng)景。假如我們?cè)跒?module 添加新功能時(shí),希望能夠加上實(shí)現(xiàn)該功能的開發(fā)人員的花名,則可以這樣構(gòu)造裝飾器(以 功能5 為例):

      def func_5_with_name(name=None):     def func_5(original_module):         def wrapper():             original_module()             print('功能5由{}實(shí)現(xiàn)'.format(name))         return wrapper    return func_5

      效果如下:

      @func_5_with_name(name='若水')def module():     print('功能1')     print('功能2')     print('功能3')     print('功能4')module()# 功能1# 功能2# 功能3# 功能4# 功能5由若水實(shí)現(xiàn)

      對(duì)于這種三層嵌套函數(shù),我們可以這樣理解:當(dāng)為 func_5_with_name 指定了參數(shù)后,func_5_with_name(name='若水') 實(shí)際上返回了一個(gè) decorator,于是 @func_5_with_name(name='若水') 就相當(dāng)于 @decorator。

      六、使用類作為裝飾器

      將類作為裝飾器,我們需要實(shí)現(xiàn) __init__ 方法和 __call__ 方法。

      以計(jì)時(shí)器為例,具體實(shí)現(xiàn)如下:

      class Timer:      def __init__(self, func):         self.func = func    def __call__(self):         import time         tic = time.time()         self.func()         toc = time.time()         print('用時(shí): {}s'.format(toc - tic))@Timerdef make_list():     return [i**2 for i in range(10**7)]make_list()# 用時(shí): 2.928966999053955s

      如果想要自定義生成列表的長(zhǎng)度并獲得列表(即被裝飾的函數(shù)帶有參數(shù)情形),我們就需要在 __call__ 方法中傳入相應(yīng)的參數(shù),具體如下:

      class Timer:      def __init__(self, func):         self.func = func    def __call__(self, num):          import time         tic = time.time()         res = self.func(num)         toc = time.time()         print('用時(shí): {}s'.format(toc - tic))          return res@Timerdef make_list(num):     return [i**2 for i in range(num)]my_list = make_list(10**7)# 用時(shí): 2.8219943046569824sprint(len(my_list))# 10000000

      如果要構(gòu)建帶參數(shù)的類裝飾器,則不能把 func 傳入 __init__ 中,而是傳入到 __call__ 中,同時(shí) __init__ 用來初始化類裝飾器的參數(shù)。

      接下來我們使用類裝飾器來復(fù)現(xiàn)第五章節(jié)中的效果:

      class Func_5:      def __init__(self, name=None):         self.name = name    def __call__(self, func):          def wrapper():             func()             print('功能5由{}實(shí)現(xiàn)'.format(self.name))          return wrapper@Func_5('若水')def module():     print('功能1')     print('功能2')     print('功能3')     print('功能4')module()# 功能1# 功能2# 功能3# 功能4# 功能5由若水實(shí)現(xiàn)

      七、內(nèi)置裝飾器

      Python中有許多內(nèi)置裝飾器,這里僅介紹最常見的三種:@classmethod、@staticmethod@property。

      7.1 @classmethod

      @classmethod 用于裝飾類中的函數(shù),使用它裝飾的函數(shù)不需要進(jìn)行實(shí)例化也可調(diào)用。需要注意的是,被裝飾的函數(shù)不需要 self 參數(shù),但第一個(gè)參數(shù)需要是表示自身類的 cls 參數(shù),它可以來調(diào)用類的屬性,類的方法,實(shí)例化對(duì)象等。

      cls 代表類本身,self 代表實(shí)例本身。

      具體請(qǐng)看下例:

      class A:      num = 100      def func1(self):         print('功能1')      @classmethod     def func2(cls):         print('功能2')         print(cls.num)         cls().func1()A.func2()# 功能2# 100# 功能1

      7.2 @staticmethod

      @staticmethod 同樣用來修飾類中的方法,使用它裝飾的函數(shù)的參數(shù)沒有任何限制(即無需傳入 self 參數(shù)),并且可以不用實(shí)例化調(diào)用該方法。當(dāng)然,實(shí)例化后調(diào)用該方法也是允許的。

      具體如下:

      class A:      @staticmethod     def add(a, b):         return a + bprint(A.add(2, 3))# 5print(A().add(2, 3))# 5

      7.3 @property

      使用 @property 裝飾器,我們可以直接通過方法名來訪問類方法,不需要在方法名后添加一對(duì) () 小括號(hào)。

      class A:      @property     def printer(self):         print('Hello World')a = A()a.printer# Hello World

      除此之外,@property 還可以用來防止類的屬性被修改??紤]如下場(chǎng)景

      class A:      def __init__(self):         self.name = 'ABC'a = A()print(a.name)# ABCa.name = 1print(a.name)# 1

      可以看出類中的屬性 name 可以被隨意修改。如果要防止修改,則可以這樣做

      class A:      def __init__(self):         self.name_ = 'ABC'      @property     def name(self):         return self.name_           a = A()print(a.name)# ABCa.name = 1print(a.name)# AttributeError: can't set attribute

      推薦學(xué)習(xí):python視頻教程

      贊(0)
      分享到: 更多 (0)
      網(wǎng)站地圖   滬ICP備18035694號(hào)-2    滬公網(wǎng)安備31011702889846號(hào)