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. 簡單的裝飾器

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),簡單明了
@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)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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