方法装饰器的Pythonic应用?

方法装饰器的Pythonic应用?,python,python-3.x,decorator,Python,Python 3.x,Decorator,我试图理解如何编写一个decorator来检查每个方法的main_val属性,特别是当main_val属性的位置可能会改变,甚至有一个默认值时。另外,是否有一种更适合应用这种条件的方法 我在下面写了一个例子来直观地描述这个问题,希望有更多经验的人能够解释 对于下面的每个方法,main_val应该是我的decorator应该检查的唯一属性。我不确定如何强制装饰器始终检查main_val,除非它作为key=value属性传递或引用*args中的硬编码位置 class TestCase: de

我试图理解如何编写一个decorator来检查每个方法的main_val属性,特别是当main_val属性的位置可能会改变,甚至有一个默认值时。另外,是否有一种更适合应用这种条件的方法

我在下面写了一个例子来直观地描述这个问题,希望有更多经验的人能够解释

对于下面的每个方法,main_val应该是我的decorator应该检查的唯一属性。我不确定如何强制装饰器始终检查main_val,除非它作为key=value属性传递或引用*args中的硬编码位置

class TestCase:
    def __init__(self):
        self.allowable_list = [1,2,3,4,5]

    def decorator_check(func):
        # I want to apply condition to "main_val" attribute of any method this decorator is applied to
        def wrapper(self, val, *args, **kwargs):
            if val not in self.allowable_list:
                raise AttributeError("value {} not in allowable list {}".format(val, self.allowable_list))
        return func(self, val, *args, **kwargs)
    return wrapper

    @decorator_check
    def calc(self, main_val, val_num_two, val_num_three):
        print("(calc) - main_val: {} val_num_two: {} val_num_three: {}".format(main_val, val_num_two, val_num_three))
        return main_val * val_num_two * val_num_three

    @decorator_check
    def calc_two(self, another_val, main_val,  val_num_two):
        print("(calc_two) - another_val: {} main_val: {} val_num_two: {}".format(another_val, main_val, val_num_two))
        return another_val * main_val * val_num_two

    @decorator_check
    def calc_three(self, another_val, val_num_two, main_val=3):
        print("(calc_two) - another_val: {} main_val: {} val_num_two: {}".format(another_val, main_val, val_num_two))
        return another_val * val_num_two * main_val

test_obj = TestCase()

按预期返回错误

test_obj.calc_two(2, 5, 3)
返回(计算二)-另一个值:2主值:5值数:3

test_obj.calc_three(2,5,100)
返回值(计算值2)-另一个值:2值数:5主值:100

解决此问题的方法有哪些?

用于确定签名、绑定参数、应用默认值以及计算
main\u val
的最终值:

import functools
import inspect

def main_val_decorator(f):
    f_sig = inspect.signature(f)
    @functools.wraps(f)
    def wrapper(*args, **kwargs):
        bound = f_sig.bind(*args, **kwargs)
        bound.apply_defaults()
        main_val = bound.arguments['main_val']

        do_whatever_with(main_val)
        return f(*args, **kwargs)
    return wrapper
这做的工作比您需要的要多,因为它也为所有其他参数确定绑定,但比手动执行内省方便得多。

用于确定签名、绑定参数、应用默认值以及计算
main\u val
的最终值:

import functools
import inspect

def main_val_decorator(f):
    f_sig = inspect.signature(f)
    @functools.wraps(f)
    def wrapper(*args, **kwargs):
        bound = f_sig.bind(*args, **kwargs)
        bound.apply_defaults()
        main_val = bound.arguments['main_val']

        do_whatever_with(main_val)
        return f(*args, **kwargs)
    return wrapper

这做的工作比您需要的要多,因为它也确定了所有其他参数的绑定,但它比手动执行内省方便得多。

这与您的主要问题无关,但在您使用它的情况下,
AttributeError肯定不是正确的例外。该错误表明属性查找
obj.attr
失败,因为
attr
不存在。我认为您可能应该使用
ValueError
,因为当您将错误值的参数传递给函数或方法时,通常会出现这种情况。由于您的装饰程序正在根据允许的值列表检查您传递的值,这似乎是最合适的。谢谢@Blckknght!我只是查了一下定义,没有意识到是这样的。这与你的主要问题无关,但在你使用它的情况下,
AttributeError
肯定不是正确的例外。该错误表明属性查找
obj.attr
失败,因为
attr
不存在。我认为您可能应该使用
ValueError
,因为当您将错误值的参数传递给函数或方法时,通常会出现这种情况。由于您的装饰程序正在根据允许的值列表检查您传递的值,这似乎是最合适的。谢谢@Blckknght!我只是查了一下定义,并没有意识到是这样的。这有点过分了——但还是有用的。希望有一个更简单的解决方案,如果你想处理一个可能在方法签名中任何地方的值,我想这大概是最简单的。对于较窄的情况(例如,如果参数仅为关键字),您可以提出自己的自定义解决方案,该解决方案可能更简单,但这可能是唯一好的通用解决方案。感谢各位-有一点我不太明白,functools.wrapps在这种情况下有什么意义?似乎没有必要这有点过分了,但还是有用的。希望有一个更简单的解决方案,如果你想处理一个可能在方法签名中任何地方的值,我想这大概是最简单的。对于较窄的情况(例如,如果参数仅为关键字),您可以提出自己的自定义解决方案,该解决方案可能更简单,但这可能是唯一好的通用解决方案。感谢各位-有一点我不太明白,functools.wrapps在这种情况下有什么意义?似乎没有必要