在Python中使用元类填充工厂

时间:2013-05-14 14:59:09

标签: python factory-pattern metaclass

显然,在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. 自动导入整个项目中的所有内容(这令我感到非常笨拙和笨拙)。
  3. 在这些选项中,将类直接注册到工厂似乎是最好的选择。有没有人找到一个我没见过的更好的解决方案?一个选项可能是通过遍历项目文件自动生成工厂模块中所需的导入,但除非您使用commit-hook执行此操作,否则您将面临工厂过时的风险。

    更新

    我已经发布了一个自我回答,关闭它。如果有人知道以一种永远不会出现循环的方式遍历嵌套子包中的所有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。由于Sub中的__init__.py被命中,它会尝试导入S1,S2和S3。但是,S1想要导入A(它还没有存在,因为它正在导入过程中)!因此导入失败。您可以切换遍历的发生方式(即,首先是深度而不是先进行广度),但是您遇到了相同的问题。任何关于良好遍历方法的见解都会非常有帮助。两阶段方法可以解决它(即,遍历以获取所有模块引用,然后作为平面批处理导入)。但是,我不太确定处理最后阶段的最佳方法(即,知道何时完成遍历然后导入所有内容)。我的主要限制是我不想要一个超级包来处理(即Sub和A下的额外目录)。如果我有这个,它可以启动遍历,但所有东西都需要相对于那个导入没有充分的理由(即,所有导入更长的额外目录)。因此,添加对sitecustomize.py的特殊函数调用似乎是我唯一的选择(无论如何我在该文件中设置了包开发的根目录。)

1 个答案:

答案 0 :(得分:0)

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

这种方法的缺点是有时会多次导入相同的模块,如果有人在模块导入中留下带副作用的代码,这会很烦人。但是,这两种方式都很糟糕。不幸的是,如果你这样做,一些主要的包(咳嗽,咳嗽:烧瓶)会对IDLE产生严重的抱怨(IDLE只是重新开始,而不是做任何事情)。另一个缺点是因为模块互相导入,有时它会尝试导入一个已经处于导入过程中的模块(一个容易被捕获的错误,但我仍然试图消除它)。这不是理想的,但它确实完成了工作。附上更具体问题的其他细节,如果有人能提供更好的答案,我很乐意接受。