【问题标题】:Calling decorated external functions调用修饰的外部函数
【发布时间】:2018-01-11 11:46:36
【问题描述】:

我正在寻找一种方法让脚本执行一些我不知道名称并在另一个模块上定义的函数(假设由另一个用户提供),但用我定义的函数进行修饰。

例如,外部脚本是:

    from MyScript import preprocess, process, postprocess 

    @preprocess(foo, bar)
    def external_provider_random_name():
        return "foo"

    @process(foobar)
    def external_provider_random():
        return "bar"

    @postprocess(foo, bar)
    def external_provider():
        return "foobar"

然后我会在我的应用程序中导入它并执行函数(我不知道它们的名字),并能够使用修饰函数返回的内容。

我想到装饰器的原因是成为flask似乎暴露了类似的行为: 用户可以通过使用$@app.route(my/route) 装饰函数来定义新路由。然后在路由上执行 http 调用时执行此函数,并且应用程序可以使用该函数返回的任何内容(一个 json)将其返回给用户。

但是,这种行为似乎与装饰器的实际定义不符,我不知道如何实现这一点。我在这里遗漏了什么还是只使用装饰器的解决方案?

【问题讨论】:

  • 请注意,在函数后添加function = decorator(function) 等同于在函数前添加@decorator
  • 这就是我可以围绕函数调用添加操作的方式。但是我仍然不知道如何首先调用该函数,因为我无权访问它的名称。
  • 你不需要函数名来调用它。您只需要对该函数的引用,这正是传递给您的装饰器的内容。
  • 基本上你的装饰器必须将函数存储在某个地方的注册表中。没有魔法设施装饰器可以做到这一点;您只需要自己编写一个装饰器,它会执行类似my_registry.append(func) 的操作(其中my_registry 是您创建的一些函数列表,func 是被装饰的函数)。
  • 从根本上说,flask app 对象不仅仅是一个装饰功能,它是一个巨大的类。 app.route 方法 是实际的装饰器,它基本上是adds the function you are calling to a registry

标签: python decorator


【解决方案1】:

你只需要从你的包装器中取出传入函数的副本。您可以将函数引用存储在全局名称、listdict 或您选择的任何其他方式中。

这是一个 MyScript.py 的示例,它将其函数引用存储在 dict 中:

funcs = { }

def preprocess(*args):
    def wrapper(fn):
        funcs['preprocess'] = fn
        return fn
    return wrapper

def process(*args):
    def wrapper(fn):
        funcs['process'] = fn
        return fn
    return wrapper

def postprocess(*args):
    def wrapper(fn):
        funcs['process'] = fn
        return fn
    return wrapper

所有初始化完成后,就可以调用函数了:

MyScript.funcs['preprocess']()

【讨论】:

  • 此时,就像在flask 中一样,使用类来封装状态可能是有意义的,其中可调用的装饰器实际上是对象上的方法。
  • 同意。我只是选择了一个全局的dict 作为一个易于理解的例子。
【解决方案2】:

您似乎想要创建某种函数注册表。这是一个简单的示例实现,它使用字典来跟踪注册的函数。

class Tracker(dict):
    def register(self, identifier):
        def wrapper(function):
            self[identifier] = function
            return function
        return wrapper


tracker = Tracker()

@tracker.register('A')
def function_A():
    print('Blah')

tracker['A']() # Blah

如果要注册一个已经定义好的函数,可以使用:

tracker.register('A')(function_A)

【讨论】:

  • 我非常喜欢这个主意。如果 function_A 的定义在脚本之外,在外部脚本上,这将如何工作 - 我们称之为 external.py ?
  • @AugBar 我不确定我明白你的意思,但我已经更新了答案以解决这个问题。
  • 该函数与tracker的实例化定义在同一范围内。我需要在外面,在一个单独的文件上完成。但我认为我有解决方案,就像使用烧瓶和蓝图一样!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-09-17
  • 1970-01-01
  • 2017-08-01
  • 2018-06-12
  • 2018-04-22
  • 2014-02-04
相关资源
最近更新 更多