【问题标题】:Pythonic application of method decorators?方法装饰器的 Pythonic 应用?
【发布时间】:2018-02-01 18:23:22
【问题描述】:

我试图了解如何编写一个装饰器来检查每个方法的 main_val 属性,尤其是当 main_val 属性的位置可能发生变化,甚至有默认值时。另外,有没有更 Pythonic 的方式来应用这个条件?

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

对于下面的每个方法,main_val 应该是我的装饰器应该检查的唯一属性。我不确定如何强制装饰器始终检查 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(100, 2 ,3)

按预期返回错误

test_obj.calc_two(2, 5, 3)

返回 (calc_two) - another_val: 2 main_val: 5 val_num_two: 3

test_obj.calc_three(2,5,100)

返回 (calc_two) - another_val: 2 val_num_two: 5 main_val: 100

有什么方法可以解决这个问题?

【问题讨论】:

  • 这与您的主要问题无关,但AttributeError 在您使用它的情况下肯定不是正确的例外。该错误表明属性查找obj.attr 失败,因为attr 不存在。我认为您可能应该改用ValueError,因为当您将错误值的参数传递给函数或方法时,通常会引发这种情况。由于您的装饰器正在根据允许值列表检查您传递的值,这似乎是最合适的。
  • 谢谢@Blckknght!我只是查看了定义,并没有意识到是这样的。

标签: python python-3.x decorator


【解决方案1】:

使用inspect.signature 确定签名、绑定参数、应用默认值并计算main_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

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

【讨论】:

  • 这有点矫枉过正 - 但仍然有用。希望也许有更直接的解决方案
  • 我怀疑如果你想处理一个可能在方法签名中任何位置的值,这将是最简单的。对于更狭窄的情况(例如,如果参数是仅关键字),您可以提出自己的自定义解决方案,该解决方案可能更简单,但这可能是唯一好的通用解决方案。
  • 谢谢大家 - 我不太明白一件事,在这种情况下 functools.wraps 有什么意义?好像没必要
猜你喜欢
  • 2020-01-11
  • 1970-01-01
  • 2014-01-14
  • 1970-01-01
  • 2011-10-05
  • 2016-03-17
  • 2016-03-23
  • 2016-07-24
  • 1970-01-01
相关资源
最近更新 更多