【问题标题】:How to make python decorators work like a tag to make function calls "by tag"如何使 python 装饰器像标签一样工作以“按标签”进行函数调用
【发布时间】:2017-06-09 15:01:51
【问题描述】:

我是一名 Python 初学者,对装饰器的基本概念有所了解。我之前研究过 Python-Behave,该框架允许您插入一个装饰器,它的功能就像一个标签。我正在尝试在我目前正在构建的框架中复制它。

CLI 中的自定义参数

parser = argparse.ArgumentParser()
parser.add_argument('--type', help='foo help')
args = parser.parse_args() #args.type would receive the value

CLI

python run.py --type=A

功能

   @typeA
   def foo_func():
      print "type A ran"

   @typeB
   def bar_func():
      print "type B ran"

预期输出

"type A ran"

【问题讨论】:

  • 从您的问题中不太清楚问题是什么,因此您可能需要添加一些信息。阅读documentation on function definition 也是一个好主意。
  • 如果你这样做,你需要一个单独的装饰器函数来处理参数的每个可能值。像@type('A')@type('B') 这样的东西可能是一个更好的方法——尽管最好使用不同的名称,因为“类型”是一个内置的对象名称。

标签: python decorator python-decorators


【解决方案1】:

使用装饰器在字典中存储对函数的引用。使用用户输入从字典中检索函数。之后调用它。

REGISTER = {}

def register(name):
    def wrapper(f):
        print f, 'registered'
        REGISTER[name] = f
        return f
    return wrapper

@register('A')
def foo():
    print 'foo called'

@register('B')
def bar():
    print 'bar called'

name = 'A'  # or args.type
func_to_call = REGISTER[name]
func_to_call()  # actual call is done here

【讨论】:

    【解决方案2】:

    将您的装饰器包装成类风格的装饰器以避免悬挂变量并使用记忆工厂函数来创建标签。

    import functools
    
    class TagDecorator(object):
    
        def __init__(self, tagName):
            self.functions = []
            self.tagName = tagName
    
        def __str__(self):
            return "<TagDecorator {tagName}>".format(tagName=self.tagName)
    
        def __call__(self, f):
            self.functions.append(f)
            return f
    
        def invoke(self, *args, **kwargs):
            return [f(*args, **kwargs) for f in self.functions]
    
    
    @functools.lru_cache(maxsize=None)  # memoization
    def get_func_tag(tagName):
        return TagDecorator(tagName)
    

    现在我们创建我们的标签:

    tagA = get_func_tag("A")
    tagB = get_func_tag("B")
    
    @tagA
    def funcA_1(*args, **kwargs):
        print("A", args, kwargs)
    
    # another way
    @get_func_tag("A")
    def funcA_2(*args, **kwargs):
        print("A too", args, kwargs)
    
    @tagB
    def funcB_1():
        print("B")
    
    @tagB
    def funcB_2():
        print("B too")
    
    # invoke all functions registered with tagA : passing arguments
    tagA.invoke("hello", who="dolly")
    
    # invoke all functions registered with tagA, another way.
    get_func_tag("A").invoke()
    
    # actually get_func_tag always returns the same instance
    # for a given tagName, thanks to lru_cache
    assert get_func_tag("A") == tagA
    
    # Of course tagB can be invoked
    tagB.invoke()
    get_func_tag("B").invoke()
    
    # but passing it args would be an error.
    tagB.invoke("B")  # TypeError: funcB_1() takes 0 positional arguments but 1 was given
    

    必须注意标记函数的参数。如果对于一个相同的标签,您注册的签名不同,那么在调用它们时肯定会遇到问题。您可以使用inspect.signature 函数来解决这个问题。但是真正的签名匹配有点棘手。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-04-20
      • 1970-01-01
      • 1970-01-01
      • 2011-08-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多