【问题标题】:How do I get the value of an argument passed to a decorator in Python?如何在 Python 中获取传递给装饰器的参数值?
【发布时间】:2018-08-01 17:56:02
【问题描述】:

鉴于下面的代码,我试图识别发送给类使用的装饰器的参数。

具体来说,我正在尝试识别传递给make_hyper 装饰器的值100,该装饰器在Dog 类中使用。

理想情况下,我需要能够在不直接运行该方法的情况下获得该值,因为我正在使用的实际方法代码需要很长时间才能运行。

import inspect

def make_hyper(new_volume):
    def decorator(decorated_method):
        def wrapped_method(self, *args, **kwargs):
            self.volume = new_volume
            return decorated_method(self, *args, **kwargs)
        return wrapped_method
    return decorator


class Dog(object):
    def __init__(self, name):
        self.name = name
        self.volume = 1

    @make_hyper(new_volume=100)
    def bark(self):
        if self.volume >= 10:
            print('[{}]: BARK!!'.format(self.name))
        elif self.volume >= 5:
            print('[{}]: Bark!'.format(self.name))
        else:
            print('[{}]: Bark'.format(self.name))

我尝试使用 inspect.getargspec 和其他一些东西,并且(尽我所能)搜索 Stackoverflow 和整个互联网,但我找不到解决方案。

任何帮助将不胜感激!感谢您的宝贵时间!

更新: 有人要求我澄清我想要做什么。抱歉没有更清楚。

上述玩具所代表的实际代码是一个测试自动化框架,其中包括指定测试标签的装饰器:

class TestTags(object):
  WIFI = 'wifi'
  BLE = 'ble'
  NIGHTLY = 'nightly'
  REGRESSION = 'regression'

class TestBase(unittest.TestCase):
  <define common test stuff and test decorators>

class ThingTester(TestBase):
  @TestBase.tags(TestTags.WIFI, TestTags.BLE, TestTags.REGRESSION)
  def test_all_the_things(self):
    <test all the things>

# what I'm trying to get
test_tags = ???
print(test_tags)  # prints out ('wifi', 'ble', 'regression')

我编写了一个实用程序,它遍历所有测试模块、类和单个测试,并创建一个定义测试计划的 HTML+JavaScript 页面。我要添加到页面的一条数据是与每个测试相关联的标签。

我发现我可以让tags 装饰器将属性保存到类(即self.tags = tags),但这需要在将值保存到TestBase 对象之前运行测试,我需要能够独立于运行测试生成测试计划。

【问题讨论】:

  • 你没有展示你期望如何获取和使用装饰器的参数值,而不是通常在装饰器自己的体内。你能澄清一下你正在尝试做什么并且没有工作吗?

标签: python decorator inspect


【解决方案1】:

我不确定我是否完全理解您想要什么,但如果您打算检查现有测试以创建计划或报告,那么您可能希望将装饰器参数存储在测试函数中,而不是类实例,对吧?例如:

def TestTags(*tags):
    def decorator(f):
        def wrapper(*args, **kwargs):
            # ...
            return f(*args, **kwargs)
        wrapper.tags = tags
        return wrapper
    return decorator

@TestTags(TAG1, TAG2)
def myTest(...):
    ...

print(myTest.tags)
>>> (TAG1, TAG2)

编辑:刚刚意识到实例方法不能开箱即用。你仍然可以像这样使用它:

def TestTags(*tags):
    def decorator(f):
        def wrapper(*args, **kwargs):
            # ...
            return f(*args, **kwargs)
        wrapper.tags = tags
        return wrapper
    return decorator

class TestSuite(...):
    @TestTags(TAG1, TAG2)
    def myTest(...):
        ...

print(TestSuite.myTest.tags)  # From class function
>>> (TAG1, TAG2)
myTestSuite = TestSuite()
print(myTestSuite.myTest.__func__.tags)  # From instance method
>>> (TAG1, TAG2)

【讨论】:

  • 这很棒,几乎是我需要的。但是,如果发送到测试框架的标签与装饰器的参数不匹配,我的 tags 装饰器包含跳过测试的逻辑。我正在尝试将您的示例调整为返回 f() 而不是 f 但添加 f.tags = tags 的示例丢失了。帮忙?
  • @retsigam 啊,我明白了。我已经编辑了答案以返回一个正确的包装器,看看它是否适合你。
  • 上述修改完美解决了问题!非常感谢!
猜你喜欢
  • 1970-01-01
  • 2019-11-20
  • 2018-12-04
  • 1970-01-01
  • 2020-04-13
  • 1970-01-01
  • 2012-04-27
相关资源
最近更新 更多