模块“鸭子打字”的陷阱?

时间:2010-02-23 22:04:51

标签: python design-patterns architecture duck-typing

我刚开始尝试一种我称之为的新技术(目前至少)“模块鸭子打字”。

示例:

主要模块

import somepackage.req  ## module required by all others
import abc
import Xyz

模块abc

__all__=[]

def getBus():
    """ Locates the `req` for this application """
    for mod_name in sys.modules:
        if mod_name.find("req") > 0:
            return sys.modules[mod_name].__dict__["Bus"]
    raise RuntimeError("cannot find `req` module")

Bus=getBus()

在模块abc中,我不需要显式import req:它可以在包层次结构中的任何位置。当然这需要一些纪律......

使用这种技术,可以轻松地在层次结构中重新定位包。

有等待我的陷阱吗?例如转向Python 3K

已更新:经过多次测试后,我决定直接在sys.path中插入包依赖项。

3 个答案:

答案 0 :(得分:4)

可能会导入包含“req”的各种模块,但您不知道它是否是您实际需要的模块:

>>> import urllib.request
>>> import tst
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "tst.py", line 12, in <module>
    Bus=getBus()
  File "tst.py", line 9, in getBus
    return sys.modules[mod_name].__dict__["Bus"]
KeyError: 'Bus'

包的重点是模块层次结构有名称空间。从任何包中查找模块名称只会导致代码在用户碰巧导入某个恰好包含名称冲突的模块的库时随机中断。

答案 1 :(得分:1)

这种技术很危险且容易出错。它可以与您的测试一起使用,直到有人导入新的something.req并获得令人困惑的远距离错误。 (这是最好的情况;当前的实现会跳转到许多其他模块。)如果你重新构建包,那么很容易在那时以自动方式修改你的代码而不使用任何魔法。 Python可以做各种神奇的,动态的事情,但这并不意味着我们应该这样做。

答案 2 :(得分:0)

我认为这更像是鸭子打字。我还建议使用比“Bus”

更独特的标识符
def getBus():
    """ Locates the Bus for this application """
    for mod in sys.modules.values():
        if hasattr(mod, 'Bus') and type(mod.Bus) is...: # check other stuff about mod.Bus
            return mod.Bus
    raise RuntimeError("cannot find Bus")