真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

Python中的裝飾器知識點有哪些

這篇文章主要介紹“Python中的裝飾器知識點有哪些”,在日常操作中,相信很多人在Python中的裝飾器知識點有哪些問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”Python中的裝飾器知識點有哪些”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!

在勐海等地區(qū),都構建了全面的區(qū)域性戰(zhàn)略布局,加強發(fā)展的系統(tǒng)性、市場前瞻性、產(chǎn)品創(chuàng)新能力,以專注、極致的服務理念,為客戶提供成都網(wǎng)站建設、網(wǎng)站設計 網(wǎng)站設計制作定制開發(fā),公司網(wǎng)站建設,企業(yè)網(wǎng)站建設,品牌網(wǎng)站制作,全網(wǎng)營銷推廣,成都外貿(mào)網(wǎng)站建設,勐海網(wǎng)站建設費用合理。

Python中的裝飾器知識點有哪些

一、閉包

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

閉包,又稱閉包函數(shù)或者閉合函數(shù),通俗一點來講,當某個函數(shù)被當成對象返回時還夾帶了外部變量,就形成了一個閉包。

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

def print_msg(msg):

    def printer():
        print(msg)

    printer()print_msg('Hello World')# Hello World

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

我們再來看一下如果是閉包,該是什么樣的結構:

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ù),它夾帶了外部變量 'Hello World'

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

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


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

def outer(content):

    def inner():
        print(content)

    return innerprint(outer.__closure__)
    # Noneinner = outer('Hello World')print(inner.__closure__)
    # (,)

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

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

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

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

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

  • 局部變量無法共享和長久的保存,而全局變量可能造成變量污染,閉包既可以長久的保存變量又不會造成全局污染。

  • 閉包使得函數(shù)內(nèi)局部變量的值始終保持在內(nèi)存中,不會在外部函數(shù)調(diào)用后被自動清除。

二、裝飾器

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

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

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

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

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

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

def func_5(original_module):

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

    return wrapper

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

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

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

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

當然,Python有更簡潔的寫法(稱之為語法糖),我們可以將@符號與裝飾器函數(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ù)的基礎上完成計時任務(計算原函數(shù)的運行時間),如下:

def timer(func):

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

    return wrapper@timerdef make_list():
    return [i * i for i in range(10**7)]my_list = make_list()# 程序用時: 0.8369960784912109s

事實上,my_list 并不是列表,直接打印會顯示 None,這是因為我們的 wrapper 函數(shù)沒有設置返回值。如果需要獲得 make_list 的返回值,可以這樣修改 wrapper 函數(shù):

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

三、使用多個裝飾器

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

好在Python允許同時使用多個裝飾器:

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

上述過程實際上等價于:

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

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

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

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

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

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

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

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

因為我們的 pide 函數(shù)接收兩個參數(shù),所以我們的 wrapper 函數(shù)也應當接收兩個參數(shù):

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

使用該裝飾器進行裝飾:

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

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

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

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

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

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

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

def func_5_with_name(name=None):
    def func_5(original_module):
        def wrapper():
            original_module()
            print('功能5由{}實現(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由若水實現(xiàn)

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

六、使用類作為裝飾器

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

以計時器為例,具體實現(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('用時: {}s'.format(toc - tic))@Timerdef make_list():
    return [i**2 for i in range(10**7)]make_list()# 用時: 2.928966999053955s

如果想要自定義生成列表的長度并獲得列表(即被裝飾的函數(shù)帶有參數(shù)情形),我們就需要在 __call__ 方法中傳入相應的參數(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('用時: {}s'.format(toc - tic))

        return res@Timerdef make_list(num):
    return [i**2 for i in range(num)]my_list = make_list(10**7)# 用時: 2.8219943046569824sprint(len(my_list))# 10000000

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

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

class Func_5:

    def __init__(self, name=None):
        self.name = name    def __call__(self, func):

        def wrapper():
            func()
            print('功能5由{}實現(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由若水實現(xiàn)

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

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

7.1 @classmethod

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

cls 代表類本身,self 代表實例本身。

具體請看下例:

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ù)),并且可以不用實例化調(diào)用該方法。當然,實例化后調(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 裝飾器,我們可以直接通過方法名來訪問類方法,不需要在方法名后添加一對 () 小括號。

class A:

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

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

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

到此,關于“Python中的裝飾器知識點有哪些”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續(xù)學習更多相關知識,請繼續(xù)關注創(chuàng)新互聯(lián)網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬嵱玫奈恼拢?/p>
本文題目:Python中的裝飾器知識點有哪些
網(wǎng)站URL:http://weahome.cn/article/pjhdcd.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部