显然,在 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)
但是,有一个问题:如果我让工厂依赖元类,工厂只有在导入模块后才知道类的存在(例如,它们在模块导入时注册)。因此,要填充工厂,我必须:
- 手动导入所有相关模块(这真的会破坏让元类自动注册事物的目的......)或
- 自动导入整个项目中的所有内容(这让我觉得非常笨拙和笨拙)。
在这些选项中,将类直接注册到工厂似乎是最好的选择。有没有人找到我只是没有看到的更好的解决方案?一种选择可能是通过遍历项目文件来自动生成工厂模块中所需的导入,但除非您使用提交挂钩来执行此操作,否则您将面临工厂过时的风险。
更新:
我已经发布了一个自我答案,以关闭它。如果有人知道一种以永远不会遇到循环的方式遍历嵌套子包中所有 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 添加一个特殊的函数调用似乎是我唯一的选择(无论如何,我在该文件中设置了包开发的根目录)。