python3之裝飾器

一阻逮、裝飾器介紹

裝飾器也是一個(gè)函數(shù)粱快,它是讓其他函數(shù)在不改變變動(dòng)的前提下增加額外的功能。
裝飾器是一個(gè)閉包叔扼,把一個(gè)函數(shù)當(dāng)作參數(shù)返回一個(gè)替代版的函數(shù)事哭,本質(zhì)是一個(gè)返回函數(shù)的函數(shù)(即返回值為函數(shù)對(duì)象)。
python3支持用@符號(hào)直接將裝飾器應(yīng)用到函數(shù)瓜富。
裝飾器工作場(chǎng)景:插入日志鳍咱、性能測(cè)試、事務(wù)處理等等与柑。
函數(shù)被裝飾器裝飾過后谤辜,此函數(shù)的屬性均已發(fā)生變化蓄坏,如名稱變?yōu)檠b飾器的名稱。

1. 簡(jiǎn)單的裝飾器

1.1. 被裝飾的函數(shù)不帶參數(shù)
"""入門裝飾器:函數(shù)功能不帶參數(shù)"""
def my_decorator(func):
    def inner():
        print("**********")
        print("要添加的功能代碼")
        func()
    return inner

# script1()函數(shù)調(diào)用裝飾器的第一種方法
def script1():
    print("測(cè)試")
runScript1 = my_decorator(script1)    # 運(yùn)行script()函數(shù)的同時(shí)添加有my_decorator()函數(shù)的功能
runScript1()
# script1()函數(shù)調(diào)用裝飾器的第二種方法:使用@符號(hào)丑念,簡(jiǎn)單明了
@my_decorator
def script1():
    print("測(cè)試")
script1()
1.2. 被裝飾的函數(shù)帶參數(shù)

可變參數(shù)args和關(guān)鍵字參數(shù)*kwargs添加函數(shù)通用的裝飾器

"""入門裝飾器:函數(shù)帶參數(shù)"""
def my_decorator(func):
    def inner(*args, **kwargs):     # 可變參數(shù)*args和關(guān)鍵字參數(shù)**kwargs
        print("**********")
        print("要添加的功能代碼")
        func(*args, **kwargs)
    return inner

# script2()函數(shù)調(diào)用裝飾器的第一種方法:了解即可
def script2(arg):
    print("測(cè)試:%s" % arg)
runScript2 = my_decorator(script2)
runScript2("aaa")
# script2()函數(shù)調(diào)用裝飾器的第二種方法:使用@符號(hào)涡戳,目前使用此方法
@my_decorator
def script2(arg):
    print("測(cè)試:%s" % arg)
script2("aaa")

2. 裝飾器帶參數(shù)

"""裝飾器:裝飾器帶參數(shù)"""
def my_decorator(name):
    def outer(func):
        def inner(*args, **kwargs):
            print("********")
            print("添加帶裝飾器參數(shù)%s的功能代碼" % self.name)
            func(*args, **kwargs)
        return inner
    return outer

@my_decorator(name='settings')
def script3(arg):
    print("測(cè)試----%s" % arg)
script3("bbb")

3. 基于類封裝的裝飾器

__call __()方法是將實(shí)例成為一個(gè)可調(diào)用對(duì)象(即callable對(duì)象),同時(shí)不影響實(shí)例的構(gòu)造脯倚,但可以改變實(shí)例的內(nèi)部值渔彰。

3.1. 基于類封裝的不帶參數(shù)裝飾器

通過類封裝裝飾器的實(shí)現(xiàn)方法:先通過構(gòu)造函數(shù)__init __()傳入函數(shù);再通過__call __方法重載推正,并返回一個(gè)函數(shù)恍涂。

"""基于類封裝的不帶參數(shù)裝飾器"""
class MyDecorator:
    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        print("********")
        print("要添加的功能代碼")
        return self.func(*args, **kwargs)

@MyDecorator
def script4(arg):
    print("測(cè)試----%s" % arg)
script4("ccc")
3.2. 基于類封裝的帶參數(shù)裝飾器

通過類封裝裝飾器的實(shí)現(xiàn)方法:先通過構(gòu)造函數(shù)__init __()傳入裝飾器參數(shù);再通過__call __方法傳入被裝飾的函數(shù)舔稀,并返回一個(gè)函數(shù)乳丰。

"""基于類封裝的帶參數(shù)裝飾器"""
class MyDecorator:
    def __init__(self, name):
        self.name = name

    def __call__(self, func):
        def inner(*args, **kwargs):
            print("********")
            print("添加帶裝飾器參數(shù)%s的功能代碼" % self.name)
            func(*args, **kwargs)
        return inner

@MyDecorator(name="settings")
def script4(arg):
    print("測(cè)試----%s" % arg)
script4("ddd")

二、常用的內(nèi)置裝飾器

1. @property裝飾器

  • @property:將一個(gè)方法變?yōu)閷傩哉{(diào)用内贮。
    未添加裝飾器@property時(shí)产园,函數(shù)類型是一個(gè)方法:<class 'method'>
    添加裝飾器@property時(shí),函數(shù)類型是返回值的類型:如夜郁,<class 'str'>
  • property對(duì)象的setter方法:表示給屬性添加設(shè)置功能什燕,即可修改屬性值。
    若未添加設(shè)置屬性竞端,就設(shè)置新值屎即,則會(huì)引發(fā)錯(cuò)誤AttributeError: can't set attribute。
  • property對(duì)象的deleter方法:表示給屬性添加刪除功能
    若添加刪除屬性事富,就刪除屬性則會(huì)引發(fā)錯(cuò)誤AttributeError: can't delete attribute技俐。
"""@property裝飾器"""
class Test1:
    def __init__(self, name):
        self.__name = name

    @property               # 將函數(shù)由方法變?yōu)閷傩?    def get_name(self):
        return self.__name

    @get_name.setter            # 添加設(shè)置屬性
    def get_name(self, value):
        if not isinstance(value, str):
            raise TypeError("參數(shù)應(yīng)為字符串類型,但實(shí)際是%s類型" % type(value))
        else:
            self.__name = value

    @get_name.deleter           # 添加刪除屬性
    def get_name(self):
        del self.__name

test1 = Test1("launcher")
# 獲取get_name類型
print(type(test1.get_name))      # 結(jié)果: <class 'str'>
# 獲取get_name屬性值
print(test1.get_name)            # 結(jié)果:launcher
# 給get_name屬性設(shè)置新值:添加設(shè)置屬性需使用裝飾器@property的setter函數(shù)统台;
test1.get_name = "賦新值"
print(test1.get_name)           # 結(jié)果:賦新值
# 刪除get_name屬性:刪除屬性需使用裝飾器@property的deleter函數(shù)雕擂;
del test1.get_name
print(test1.get_name)           # 結(jié)果:報(bào)錯(cuò)(AttributeError: 'Test1' object has no attribute '_Test1__name'),表示刪除屬性成功


"""@property實(shí)例:加減法運(yùn)算"""
class Test2:
    def __init__(self, a, b):
        self.a = a
        self.b = b

    @property
    def add(self):
        return self.a + self.b

    @property
    def reduce(self):
        return self.a - self.b
print(Test2(3, 1).add)      # 結(jié)果:4
print(Test2(5, 2).reduce)   # 結(jié)果:3

2. 類對(duì)象中的方法

類對(duì)象中的方法:實(shí)例方法贱勃、類方法和靜態(tài)方法

  • 實(shí)例方法:函數(shù)中的第一個(gè)參數(shù)為self的方法
  • 靜態(tài)方法:使用@staticmethod裝飾器來將類中的函數(shù)定義為靜態(tài)方法井赌。
    類中創(chuàng)建的一些方法,但該方法并不需要引用類或?qū)嵗笕拧lo態(tài)方法通過類直接調(diào)用仇穗,無需創(chuàng)建對(duì)象,也無需傳遞self戚绕。
  • 類方法:使用@classmethod裝飾器來裝飾類中的函數(shù)定義為類方法纹坐。
    類方法不需要實(shí)例化,也不需要self參數(shù)列肢,函數(shù)中第一個(gè)參數(shù)是自身的cls參數(shù)恰画,可用來調(diào)用類的屬性宾茂、方法和實(shí)例化對(duì)象。
"""實(shí)例方法拴还、靜態(tài)方法@staticmethod跨晴、類方法@classmethod"""
class Student:
    description = "學(xué)員統(tǒng)計(jì)信息"

    def __init__(self, name, age, sex):
        self.name = name
        self.age = age
        self.sex = sex

    def function(self, fun_type):
        return fun_type

    def instance_method(self):          # 實(shí)例方法
        use_type = self.function("實(shí)例方法")
        print("------%s------" % use_type)
        print(Student.description)
        print(self.name + '_' + str(self.age) + '_' + self.sex)

    @staticmethod           # 靜態(tài)方法
    def static_method():
        student_info = Student("xiaoxiao", 20, "female")
        use_type = student_info.function("靜態(tài)方法")
        print("------%s------" % use_type)
        print(Student.description)
        print(student_info.name + '_' + str(student_info.age) + '_' + student_info.sex)

    @classmethod        # 類方法
    def class_method(cls):
        student_info = cls("xiaoming", 23, "male")
        use_type = student_info.function("類方法")
        print("------%s------" % use_type)
        print(Student.description)
        print(student_info.name + '_' + str(student_info.age) + '_' + student_info.sex)

    def call_different_method(self):
        print("------同一類對(duì)象中調(diào)用實(shí)例方法、靜態(tài)方法片林、類方法------")
        self.instance_method()
        self.static_method()
        self.class_method()

# 實(shí)例方法
Student("xiaohong", 19, "female").instance_method()
# 靜態(tài)方法
Student.static_method()
# 類方法
Student.class_method()
# 同一類對(duì)象中某個(gè)函數(shù)調(diào)用實(shí)例/靜態(tài)/類方法
Student("xiaohong", 19, "female").call_different_method()

三端盆、使用三方已封裝的裝飾器

  • 三方模塊decorator
    先安裝decorator模塊,再導(dǎo)入from decorator import decorator
  • 三方模塊wrapt
    先安裝wrapt模塊费封,再導(dǎo)入import wrapt
# decorator三方模塊
from decorator import decorator
@decorator
def My_decorator(func, *args, **kwargs):
    print("********")
    print("添加封裝的功能內(nèi)容")
    return func(*args, **kwargs)

@My_decorator
def testScript2():
    print("待裝飾的函數(shù)")
testScript2()


# wrapt三方模塊
import wrapt

"""裝飾器不帶參數(shù)"""
@wrapt.decorator
def My_decorator(wrapped, instance, args, kwargs):    
# instance參數(shù)即使用不使用也必須保留
    print("********")
    print("添加封裝的功能內(nèi)容")
    return wrapped(*args, **kwargs)

@My_decorator
def testScript1():
    print("待裝飾的函數(shù)")
testScript1()

"""裝飾器帶參數(shù)"""
def My_decorator(name):  
    @wrapt.decorator
    def inner(wrapped, instance, args, kwargs):
        print("********")
        print("添加封裝的功能內(nèi)容焕妙,且裝飾器參數(shù)為%s" % name)
        return wrapped(*args, **kwargs)
    return inner

@My_decorator(set)
def testScript1():
    print("待裝飾的函數(shù)")
testScript1()
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市弓摘,隨后出現(xiàn)的幾起案子焚鹊,更是在濱河造成了極大的恐慌,老刑警劉巖韧献,帶你破解...
    沈念sama閱讀 217,542評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件末患,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡锤窑,警方通過查閱死者的電腦和手機(jī)璧针,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來渊啰,“玉大人探橱,你說我怎么就攤上這事』嬷ぃ” “怎么了隧膏?”我有些...
    開封第一講書人閱讀 163,912評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)嚷那。 經(jīng)常有香客問我私植,道長(zhǎng),這世上最難降的妖魔是什么车酣? 我笑而不...
    開封第一講書人閱讀 58,449評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮索绪,結(jié)果婚禮上湖员,老公的妹妹穿的比我還像新娘。我一直安慰自己瑞驱,他們只是感情好娘摔,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著唤反,像睡著了一般凳寺。 火紅的嫁衣襯著肌膚如雪鸭津。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,370評(píng)論 1 302
  • 那天肠缨,我揣著相機(jī)與錄音逆趋,去河邊找鬼。 笑死晒奕,一個(gè)胖子當(dāng)著我的面吹牛闻书,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播脑慧,決...
    沈念sama閱讀 40,193評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼魄眉,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了闷袒?” 一聲冷哼從身側(cè)響起坑律,我...
    開封第一講書人閱讀 39,074評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎囊骤,沒想到半個(gè)月后晃择,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,505評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡淘捡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評(píng)論 3 335
  • 正文 我和宋清朗相戀三年藕各,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片焦除。...
    茶點(diǎn)故事閱讀 39,841評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡激况,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出膘魄,到底是詐尸還是另有隱情乌逐,我是刑警寧澤,帶...
    沈念sama閱讀 35,569評(píng)論 5 345
  • 正文 年R本政府宣布创葡,位于F島的核電站浙踢,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏灿渴。R本人自食惡果不足惜洛波,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望骚露。 院中可真熱鬧蹬挤,春花似錦、人聲如沸棘幸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至吨悍,卻和暖如春扫茅,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背育瓜。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工葫隙, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人爆雹。 一個(gè)月前我還...
    沈念sama閱讀 47,962評(píng)論 2 370
  • 正文 我出身青樓停蕉,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親钙态。 傳聞我的和親對(duì)象是個(gè)殘疾皇子慧起,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容