Python 裝飾器裝飾類中的方法

目前在中文網上能搜索到的絕大部分關於裝飾器的教程,都在講如何裝飾一個普通的函數。本文介紹如何使用Python的裝飾器裝飾一個類的方法,同時在裝飾器函數中調用類裏面的其餘方法。本文以捕獲一個方法的異常爲例來進行說明。python

有一個類Test, 它的結構以下:app

class Test(object):
    def __init__(self):
        pass

    def revive(self):
        print('revive from exception.')
        # do something to restore

    def read_value(self):
        print('here I will do something.')
        # do something.複製代碼

在類中有一個方法read_value(),這個方法在多個地方被調用。因爲某些緣由,方法read_value有可能隨機拋出Exception致使程序崩潰。因此須要對整個方法作try ... except處理。最醜陋的作法以下面的代碼所示:函數

class Test(object):
    def __init__(self):
        pass

    def revive(self):
        print('revive from exception.')
        # do something to restore

    def read_value(self):
        try:
            print('here I will do something.')
            # do something.
        except Exception as e:
            print(f'exception {e} raised, parse exception.')
            # do other thing.
            self.revive()複製代碼

這樣寫雖然能夠解決問題,可是代碼不Pythonic。spa

使用裝飾器來解決這個問題,裝飾器函數應該寫在類裏面仍是類外面呢?答案是,寫在類外面。那麼既然寫在類外面,如何調用這個類的其餘方法呢?3d

首先寫出一個最多見的處理異常的裝飾器:rest

def catch_exception(origin_func):
    def wrapper(*args, **kwargs):
        try:
            u = origin_func(*args, **kwargs)
            return u
        except Exception:
            return 'an Exception raised.'
    return wrapper


class Test(object):
    def __init__(self):
        pass

    def revive(self):
        print('revive from exception.')
        # do something to restore

 @catch_exception
    def read_value(self):
        print('here I will do something.')
        # do something.複製代碼

這種寫法,確實能夠捕獲到origin_func()的異常,可是若是在發生異常的時候,須要調用類裏面的另外一個方法來處理異常,這又應該怎麼辦?答案是給wrapper增長一個參數:self.code

代碼變爲以下形式:cdn

def catch_exception(origin_func):
    def wrapper(self, *args, **kwargs):
        try:
            u = origin_func(self, *args, **kwargs)
            return u
        except Exception:
            self.revive() #不用顧慮,直接調用原來的類的方法
            return 'an Exception raised.'
    return wrapper


class Test(object):
    def __init__(self):
        pass

    def revive(self):
        print('revive from exception.')
        # do something to restore

 @catch_exception
    def read_value(self):
        print('here I will do something.')
        # do something.複製代碼

只須要修改裝飾器定義的部分,使用裝飾器的地方徹底不須要作修改。blog

下圖爲正常運行時的運行結果:
教程

正常運行

下圖爲發生異常之後捕獲並處理異常:

發生異常

經過添加一個self參數,類外面的裝飾器就能夠直接使用類裏面的各類方法,也能夠直接使用類的屬性。

相關文章
相關標籤/搜索