0

显然,在 Python 中注册类是元类的主要用例。在这种情况下,我有一个序列化模块,它当前使用动态导入来创建类,我更愿意用工厂模式替换它。

所以基本上,它是这样做的:

data = #(Generic class based on serial data)
moduleName = data.getModule()
className = data.getClass()
aModule = __import__(moduleName)
aClass = getattr(aModule, className)

但我希望它这样做:

data = #(Generic class based on serial data)
classKey = data.getFactoryKey()
aClass = factory.getClass(classKey)

但是,有一个问题:如果我让工厂依赖元类,工厂只有在导入模块后才知道类的存在(例如,它们在模块导入时注册)。因此,要填充工厂,我必须:

  1. 手动导入所有相关模块(这真的会破坏让元类自动注册事物的目的......)或
  2. 自动导入整个项目中的所有内容(这让我觉得非常笨拙和笨拙)。

在这些选项中,将类直接注册到工厂似乎是最好的选择。有没有人找到我只是没有看到的更好的解决方案?一种选择可能是通过遍历项目文件来自动生成工厂模块中所需的导入,但除非您使用提交挂钩来执行此操作,否则您将面临工厂过时的风险。

更新:

我已经发布了一个自我答案,以关闭它。如果有人知道一种以永远不会遇到循环的方式遍历嵌套子包中所有 Python 模块的好方法,我会很乐意接受这个答案而不是这个答案。我看到的主要问题是:

\A.py (import Sub.S2)
\Sub\S1.py (import A)
\Sub\S2.py
\Sub\S3.py (import Sub.S2)

当你尝试导入 S3 时,它首先需要导入 Main(否则它不会知道 Sub 是什么)。此时,它尝试导入 A。此时,__init__.py调用 并尝试注册 A。此时,A 尝试导入 S1。由于__init__.py在 Sub 被击中时,它会尝试导入 S1、S2 和 S3。然而,S1 想要导入 A(它还不存在,因为它正在被导入)!所以导入失败。您可以切换遍历的方式(即深度优先而不是广度优先),但您遇到了同样的问题。任何对此的良好遍历方法的见解都会非常有帮助。一个两阶段的方法可能可以解决它(即遍历以获取所有模块引用,然后作为一个平面批次导入)。但是,我不太确定处理最后阶段的最佳方法(即知道何时完成遍历然后导入所有内容)。我最大的限制是我不想有一个超级包来处理(即Sub 和A 下的额外目录)。如果我有它,它可以开始遍历,但是一切都需要导入相对于它没有充分的理由(即,所有导入都不再通过一个额外的目录)。到目前为止,向 sitecustomize.py 添加一个特殊的函数调用似乎是我唯一的选择(无论如何,我在该文件中设置了包开发的根目录)。

4

1 回答 1

0

我找到的解决方案是基于特定的基本目录对包进行所有导入,并对__init__.py所有可能具有我想要注册的类的模块的模块具有特殊功能。所以基本上,如果你导入任何模块,它首先必须导入基本目录并继续遍历每个包含类似__init__.py文件的包(即文件夹)。

这种方法的缺点是有时会多次导入相同的模块,如果有人在模块导入中留下带有副作用的代码,这会很烦人。但是,无论哪种方式都不好。不幸的是,如果您这样做(IDLE 只是重新启动,而不是做任何事情),一些主要的软件包(咳嗽,咳嗽:Flask)会严重抱怨 IDLE。另一个缺点是,因为模块相互导入,有时它会尝试导入一个已经在导入过程中的模块(一个很容易发现的错误,但我仍在试图消除一个错误)。这并不理想,但它确实完成了工作。附上有关更具体问题的其他详细信息,如果有人可以提供更好的答案,我将很乐意接受。

于 2014-05-03T02:19:40.037 回答