簡體   English   中英

在Python中,是否可以使用相同的裝飾器來裝飾類和非類方法?

[英]In Python, is it possible to decorate both a class and non-class method with the same decorator?

我有一個簡單的異常日志記錄裝飾器,當我的腳本拋出異常時,它可以方便地發送自己的電子郵件。

def logExceptions(func):
   def wrapper():
      try:
         func()
      except Exception, e:
         logger.exception(e)

   return wrapper

但是,如果我想裝飾一個類方法,我必須修改wrapper()以獲取'self',否則我會收到以下錯誤:

TypeError: wrapper() takes no arguments (1 given)

當然,在那一點上我不能用它來裝飾任何非類方法,因為這樣會發生這樣的錯誤:

TypeError: wrapper() takes exactly 1 argument (0 given)

有沒有一個干凈的方法來解決這個問題? 謝謝=)

通常的做法是定義你的包裝器,使它接受*args**kwargs並將它們傳遞給它包裝的函數。 這樣它就可以包裝任何函數。

另外,我得到的印象是你所謂的“類方法”是Python所謂的“實例方法”,而你所謂的“非類方法”就是Python所謂的“函數”。 Python中的“非類方法”(例如,實例方法)采用self參數。

實例方法, classmethodstaticmethod方法之間的區別

首先注意:靜態方法和類方法都是函數,因此標准函數規則主要適用於它們。 我理解你的問題是關於靜態方法 (沒有傳遞額外參數)和類方法 (在第一個參數中接收類)之間的區別:

class Test(object):
    def standard_method(*args, **kwargs):
        # it is instance method (first argument will be instance)
        return args, kwargs

    @classmethod
    def class_method(*args, **kwargs):
        # it is class method (first argument will be class)
        return args, kwargs

    @staticmethod
    def static_method(*args, **kwargs):
        # it is static method (receives only arguments passed explicitly)
        return args, kwargs

證據(或者說是不言自明的例子)在這里:

>>> t = Test()
>>> t.standard_method()
((<__main__.Test object at 0x0000000002B47CC0>,), {})
>>> t.class_method()
((<class '__main__.Test'>,), {})
>>> t.static_method()
((), {})

如您所見,傳遞的參數列表根據您選擇的方法類型而有所不同。 您面臨的問題是可變數量的參數

有一個解決方案 - 使用參數解包:

def some_decorator(func):
    def wrapper(*args, **kwargs):
        # do something here
        # args is a tuple with positional args, kwargs is dict with keyword args
        return func(*args, **kwargs)
    return wrapper

之后, some_decorator返回的some_decorator將接受與裝飾函數相同數量的參數。

所以這些例子都可行:

@some_decorator
def say_hello():
    print 'hello'

@some_decorator
def say_something(something):
    print something

附錄

為了給你完整的例子,如果你使用這樣的結構會很好(注意functools.wraps用法):

from functools import wraps
def some_decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        # do something here
        # args is a tuple with positional args, kwargs is dict with keyword args
        return func(*args, **kwargs)
    return wrapper

其原因在functools.wraps()文檔中列出:它保留了函數名和docstring,有效地使包裝器看起來像包裝函數(有時很有用)。

裝飾的替代方法是使用sys.excepthook ,它是一個回調sys.excepthook ,對所有未捕獲的異常進行操作,您可以為其分配自定義日志記錄功能。 好處是,您不需要刪除 (更重要的是,跟蹤)您有興趣記錄異常的每個函數。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM