簡體   English   中英

裝飾器調用實例方法

[英]Decorator to invoke instance method

我有一個方法為do_something(self,a,b,c)的類A ,另一個實例方法是對輸入進行驗證並檢查權限的實例方法can_do_something(self,a,b,c)

這是我代碼中的常見模式,我想編寫一個接受驗證函數名稱並執行測試的裝飾器。

def validate_input(validation_fn_name):
        def validation_decorator(func):
            def validate_input_action(self,*args):
                error = getattr(self,validation_fn_name)(*args)
                if not error == True:
                    raise error
                else:
                    return func(*args)
            return validate_input_action
        return validation_decorator

調用功能如下

@validate_input('can_do_something')
def do_something(self,a,b,c):
   return a + b + c

問題是我不確定如何通過驗證功能來保持self 我已經將驗證fn名稱與getattr因此可以在實例的上下文中運行fn,但是我不能對func(*args)這樣做。

那么實現這一目標的正確方法是什么?

謝謝。

編輯

因此,在@AndréLaszlo回答之后,我意識到self只是第一個參數,因此根本不需要使用getattr ,而只需傳遞*args

def validate_input(validation_fn):
    def validation_decorator(func):
        def validate_input_action(*args):
            error = validation_fn(*args)
            if not error == True:
                raise error
            else:
                return func(*args)
        return validate_input_action
    return validation_decorator

更優雅,它還支持靜態方法。

向@AndréLaszlo示例添加靜態方法證明裝飾器正在工作:

 class Foo(object):
    @staticmethod
    def validate_baz(a,b,c):
       if a > b:
          return ValueError('a gt b')

    @staticmethod
    @validate_input(Foo.validate_baz)
    def baz(a,b,c):
       print a,b,c

    >>> Foo.baz(1,2,3)
    1 2 3
    >>> Foo.baz(2,1,3)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 6, in validate_input_action
    ValueError: a gt b

但是,當我嘗試在Django模型中做相同的事情時:

from django.db import models
from django.conf import settings

settings.configure()

class Dummy(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=10)

    def can_say_name(self):
        if name is None:
            return Exception('Does not have a name')

    @validate_input(can_say_name)
    def say_name(self):
        print self.name

    @staticmethod
    def can_create_dummy(name):
        if name == 'noname':
            return Exception('No name is not a name !')

    @staticmethod
    @validate_input(Dummy.can_create_dummy)
    def create_dummy(name):
        return Dummy.objects.create(name=name)

我得到以下內容:

NameError: name 'Dummy' is not defined

那么,關於這個問題,Django模型和Object之間有什么區別?

我認為這可以滿足您的需求:

def validate_input(validation_fn_name):
    def validation_decorator(func):
        def validate_input_action(self, *args):
            error = getattr(self, validation_fn_name)(*args)
            if error is not None:
                raise error
            else:
                arglist = [self] + list(args)
                return func(*arglist)
        return validate_input_action
    return validation_decorator

class Foo(object):

    def validate_length(self, arg1):
        if len(arg1) < 3:
            return ValueError('%r is too short' % arg1)

    @validate_input('validate_length')
    def bar(self, arg1):
        print "Arg1 is %r" % arg1


if __name__ == "__main__":
    f = Foo()
    f.bar('hello')
    f.bar('')

輸出為:

Arg1 is 'hello'
Traceback (most recent call last):
  File "validator.py", line 27, in <module>
    f.bar('')
  File "validator.py", line 6, in validate_input_action
    raise error
ValueError: '' is too short

更新的答案

由於validate_input裝飾器將Dummy作為參數時,尚未定義Dummy類,因此發生錯誤( NameError: name 'Dummy' is not defined )。 我想這可能會以不同的方式實現,但是現在這就是Python的工作方式。 我看到的最簡單的解決方案是堅持使用getattr ,因為它可以在運行時查找方法,所以該方法可以工作。

暫無
暫無

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

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