代码之家  ›  专栏  ›  技术社区  ›  Anurag Uniyal

如果从不同路径导入,则重新导入模块

  •  8
  • Anurag Uniyal  · 技术社区  · 16 年前

    在我正在工作的一个大型应用程序中,有几个人以不同的方式导入相同的模块,例如 输入X 或 从Y输入X 如果有人依赖全局属性,那么x的副作用将被导入两次,并可能引入非常细微的错误。

    例如,假设我有一个包mypakcage,其中包含三个文件mymodule.py、main.py和 初始化 Py

    mymodule.py内容

    l = []
    class A(object): pass
    

    主要.py内容

    def add(x):
        from mypackage import mymodule
        mymodule.l.append(x)
        print "updated list",mymodule.l
    
    def get():
        import mymodule
        return mymodule.l
    
    add(1)
    print "lets check",get()
    
    add(1)
    print "lets check again",get()
    

    资讯科技印刷

    updated list [1]
    lets check []
    updated list [1, 1]
    lets check again []
    

    因为现在在两个不同的模块中有两个列表,类似的,类A是不同的 对我来说,它看起来足够严肃了,因为类本身会受到不同的对待。 例如下面的代码打印错误

    def create():
        from mypackage import mymodule
        return mymodule.A()
    
    def check(a):
        import mymodule
        return isinstance(a, mymodule.A)
    
    print check(create())
    

    问题:

    有什么办法可以避免这种情况吗?除了执行该模块应导入单向onyl。不能用python导入机制来处理这个问题,我在django代码和其他地方也看到了一些与此相关的bug。

    2 回复  |  直到 13 年前
        1
  •  3
  •   Lennart Regebro    16 年前

    只有当main.py是您实际运行的文件时,我才能复制这个文件。在这种情况下,您将获得sys路径上main.py的当前目录。但显然您也有一个系统路径集,以便可以导入mypackage。

    在这种情况下,python不会意识到mymodule和mypackage.mymodule是同一个模块,您会得到这种效果。这一变化说明了这一点:

    def add(x):
        from mypackage import mymodule
        print "mypackage.mymodule path", mymodule
        mymodule.l.append(x)
        print "updated list",mymodule.l
    
    def get():
        import mymodule
        print "mymodule path", mymodule
        return mymodule.l
    
    add(1)
    print "lets check",get()
    
    add(1)
    print "lets check again",get()
    
    
    $ export PYTHONPATH=.
    $ python  mypackage/main.py 
    
    mypackage.mymodule path <module 'mypackage.mymodule' from '/tmp/mypackage/mymodule.pyc'>
    mymodule path <module 'mymodule' from '/tmp/mypackage/mymodule.pyc'>
    

    但在当前目录中添加另一个主文件:

    realmain.py:
    from mypackage import main
    

    结果是不同的:

    mypackage.mymodule path <module 'mypackage.mymodule' from '/tmp/mypackage/mymodule.pyc'>
    mymodule path <module 'mypackage.mymodule' from '/tmp/mypackage/mymodule.pyc'>
    

    所以我怀疑包中有您的主python文件。在这种情况下,解决办法是不这样做。-)

        2
  •  4
  •   nosklo    16 年前

    每个模块命名空间只导入一次。问题是,您导入它们的方式不同。第一个是从全局包导入,第二个是本地的非打包 import . python认为模块是不同的。第一个导入在内部缓存为 mypackage.mymodule 第二个是 mymodule 只有。

    解决这个问题的方法是 始终使用绝对导入 .也就是说,始终为模块提供从顶级包开始的绝对导入路径:

    def add(x):
        from mypackage import mymodule
        mymodule.l.append(x)
        print "updated list",mymodule.l
    
    def get():
        from mypackage import mymodule
        return mymodule.l
    

    记住你的入口点(你运行的文件, main.py )也应该是 外部 包裹。当您希望入口点代码在包中时,通常使用运行一个小脚本。例子:

    runme.py ,包装外:

    from mypackage.main import main
    main()
    

    而在 Me.Py 你补充说:

    def main():
        # your code
    

    我发现 this document JPCalderone提供了一个关于如何(而不是)构造您的Python项目的好技巧。跟着它走你就不会有问题了。注意 bin 文件夹-它在包外。我将在这里复制整个文本:

    python项目的文件系统结构

    :

    • 把目录命名为 与您的项目相关。例如, 如果您的项目命名为“ 扭曲的 “ 为其命名顶级目录 源文件 Twisted . 当你这样做的时候 版本,您应该包括一个版本 数字后缀: Twisted-2.5 .
    • 创建目录 Twisted/bin 和 把你的可执行文件放在那里,如果你 有任何。别给他们一个 .py 扩展,即使它们是python 源文件。不要输入任何代码 除了导入和调用 在别处定义的主函数 在你的项目中。
    • 如果你的项目 可表示为单个python 源文件,然后将其放入 目录并命名为 与您的项目相关。例如, Twisted/twisted.py . 如果你需要 多个源文件,创建 改为打包( Twisted/twisted/ , 带一个空的 Twisted/twisted/__init__.py ) 将源文件放入其中。为了 例子, Twisted/twisted/internet.py .
    • 放 您的单元测试在以下子包中 您的包裹(注意-这意味着 单个python源文件选项 上面是一个技巧-你总是需要 你的单位至少还有一个文件 测试)。例如, Twisted/twisted/test/ . 当然, 把它做成一个包裹 Twisted/twisted/test/__init__.py . 将测试放在类似的文件中 Twisted/twisted/test/test_internet.py .
    • 添加 Twisted/README t wisted/setup.py 解释和 分别安装软件, 如果你感觉好的话。

    不要 :

    • 将源文件放在目录中 打电话 src lib . 这使得它 不安装很难运行。
    • 放 您在Python之外的测试 包裹。这使得它很难运行 对已安装版本进行测试。
    • 创建一个只有 __init__.py 然后把你所有的 代码进入 π介子 . 只做一个 模块而不是包,它是 更简单。
    • 试着想出 让巨蟒能够 导入模块或包时不带 让用户添加目录 将其包含到其导入路径 (或者通过 PYTHONPATH 或者其他 机制)。你不会正确的 处理所有案例,用户将获得 当你的软件 在他们的环境中不工作。
    推荐文章