Python - 从导入的模块注册函数

Posted

技术标签:

【中文标题】Python - 从导入的模块注册函数【英文标题】:Python - Register functions from imported module 【发布时间】:2020-12-14 09:50:11 【问题描述】:

我正在尝试确定实现我的应用程序的最佳方式,其中将有一个主程序 (main.py) 和一个单独的规则模块 (rules.py),其中可以编写任意数量的规则,它们将都可以在 main.py 中应用来操作一些数据(字典)。应用程序的用户可以在 rules.py 中添加他们的自定义规则,而不会影响 main.py 中的逻辑。

我在想装饰器在这里可以用来注册 rules.py 中的每个函数,而 main.py 可以迭代它们,但我不确定具体的实现。这是我的骨架代码。

ma​​in.py

import rules

modifiers = [] # List of fuctions to modify data

def add_modifier(f):
  modifiers.append(f)
  return f

def invoke_modifiers(data):
  for modifier in modifiers:
    data = modifier(data)
  return data

if __name__ == "__main__":
  data = 'foo': 'bar'
  print(f"Invoking modifiers on data: data")
  data = invoke_modifiers(data)
  print(f"Done invoking modifiers: data")

rules.py

from main import add_modifier

@add_modifier
def mod1(data):
  data['foo'] = 'baz'
  return data

@add_modifier
def mod2(data):
  data['quz'] = 'qux'
  return data

但是当我执行代码时,它并没有修改我的数据。

$ python main.py
Invoking modifiers on data: 'foo': 'bar'
Done invoking modifiers: 'foo': 'bar'

所以我的问题有两个:

    这是在主应用程序之外使用用户定义函数的好方法吗? 在rules.py模块中需要修改哪些数据才能得到mod1和mod2修改的数据?

编辑

如果我在 rules.py 中省略 from main import add_modifier,我会在执行期间得到以下信息:

Traceback (most recent call last):
  File "main.py", line 3, in <module>
    import rules
  File "/home/telorb/Python/registerTest/rules.py", line 3, in <module>
    @add_modifier
NameError: name 'add_modifier' is not defined

【问题讨论】:

看起来你有一个循环导入。这可能会在您的 rules.py 中引发错误。 查看我的编辑以获取从 rules.py 中省略导入语句的结果。还有其他方法吗? 【参考方案1】:

Python 导入机制相当复杂,但在这种情况下,解释为什么原始代码不起作用很简单。

当调用main.py 时,其模块名称为"__main__" 而不是"main"。当rules 被导入并反过来导入main 时,会创建一个名为"main"(无下划线)的新模块,它有自己的modifiers 实例。您可以通过将 print("modifiers id = ", id(modifiers))add_modifier 放入主块中轻松检查这一点。

我认为这里最好的解决方案是创建一个管理修饰符的新模块:

ma​​in.py

from modifiers import invoke_modifiers
import rules

if __name__ == "__main__":
  data = 'foo': 'bar'
  print(f"Invoking modifiers on data: data")
  data = invoke_modifiers(data)
  print(f"Done invoking modifiers: data")

rules.py

from modifiers import add_modifier

@add_modifier
def mod1(data):
  data['foo'] = 'baz'
  return data

@add_modifier
def mod2(data):
  data['quz'] = 'qux'
  return data

modifiers.py

_modifiers = [] # List of fuctions to modify data

def add_modifier(f):
  _modifiers.append(f)
  return f


def invoke_modifiers(data):
  for modifier in _modifiers:
    data = modifier(data)
  return data

结果

azelcer@ZX80:~$ python main.py
Invoking modifiers on data: 'foo': 'bar'
Done invoking modifiers: 'foo': 'baz', 'quz': 'qux'

【讨论】:

【参考方案2】:

现在我将使用此处找到的解决方案:How can I decorate all functions imported from a file?

ma​​in.py

import types
import functools

modifiers = [] # List of fuctions to modify data

def decorate_all_in_module(module, decorator):
    for name in dir(module):
        obj = getattr(module, name)
        if isinstance(obj, types.FunctionType):
            setattr(module, name, decorator(obj))

def add_modifier(f):
  print(f"Adding modifier: f")
  modifiers.append(f)
  return f

def invoke_modifiers(data):
  print(f'Invoking len(modifiers) modifiers')
  for modifier in modifiers:
    print(f'Invoking modifier')
    data = modifier(data)
  return data

@add_modifier
def mod_main(data):
  data['xray'] = 'zulu'
  return data

import rules

if __name__ == "__main__":
  decorate_all_in_module(rules, add_modifier)
  data = 'foo': 'bar'
  print(f"Invoking modifiers on data: data")
  data = invoke_modifiers(data)
  print(f"Done invoking modifiers: data")

rules.py

def mod1(data):
  data['foo'] = 'baz'
  return data

def mod2(data):
  data['quz'] = 'qux'
  return data

$ python main.py

Adding modifier: <function mod_main at 0x7fe69ce75160>
Adding modifier: <function mod1 at 0x7fe69ce75040>
Adding modifier: <function mod2 at 0x7fe69ce75700>
Invoking modifiers on data: 'foo': 'bar'
Invoking 3 modifiers
Invoking <function mod_main at 0x7fe69ce75160>
Invoking <function mod1 at 0x7fe69ce75040>
Invoking <function mod2 at 0x7fe69ce75700>
Done invoking modifiers: 'foo': 'baz', 'xray': 'zulu', 'quz': 'qux'

【讨论】:

【参考方案3】:

我对装饰器不是很熟悉,所以也许其他人可以就此提供建议。

但考虑到您正在做的事情,我认为为用户创建一个类来添加功能将拥有您正在寻找的功能。

这个帖子的东西:Is there a way to loop through and execute all of the functions in a Python class?

一个建议的解决方案确实使用了装饰器,因此可以阐明如何更好地构建您的结构。

【讨论】:

以上是关于Python - 从导入的模块注册函数的主要内容,如果未能解决你的问题,请参考以下文章

Python 2.7函数导入[重复]

Python 模块导入

PYTHON学习0040:函数---模块的种类和导入方法--2019-7-27

Python基础

python导入模块的方法都有哪些

从__init__.py中的函数导入模块将模块对象绑定到全局命名空间?