【问题标题】:module reimported if imported from different path如果从不同路径导入,则重新导入模块
【发布时间】:2009-09-22 10:17:31
【问题描述】:

在我正在工作的一个大型应用程序中,几个人以不同的方式导入相同的模块,例如 进口 x 或者 从 y 导入 x 这样做的副作用是 x 被导入两次,如果有人依赖全局属性,可能会引入非常微妙的错误

例如假设我有一个包含三个文件 mymodule.py、main.py 和 init.py

的包 mypakcage

mymodule.py 内容

l = []
class A(object): pass

ma​​in.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类是不同的 对我来说,这看起来很严肃,因为课程本身会受到不同的对待 例如下面的代码打印 False

def create():
    from mypackage import mymodule
    return mymodule.A()

def check(a):
    import mymodule
    return isinstance(a, mymodule.A)

print check(create())

问题:

有什么办法可以避免这种情况吗?除了强制该模块应该以一种方式导入外。这不能通过python导入机制来处理吗,我在django代码和其他地方也看到了一些与此相关的错误。

【问题讨论】:

    标签: python python-import


    【解决方案1】:

    每个模块命名空间只导入一次。问题是,您以不同的方式导入它们。第一次是从全局包中导入,第二次是本地的、未打包的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()
    

    然后在main.py 中添加:

    def main():
        # your code
    

    我发现 Jp Calderone 的 this document 是关于如何(不)构建你的 python 项目的一个很好的提示。跟着它,你不会有问题。注意bin 文件夹——它在包外。我将在此处复制全文:

    Python 项目的文件系统结构

    • 为目录命名 与您的项目有关。例如, 如果您的项目名为“Twisted”, 为其命名顶级目录 源文件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和Twisted/setup.py解释和 分别安装您的软件, 如果你感觉不错的话。

    不要

    • 将您的源代码放在一个目录中 称为srclib。这使它 不安装很难运行。
    • 放 你在 Python 之外的测试 包裹。这使得很难运行 针对已安装的版本进行测试。
    • 创建一个只有一个 __init__.py 然后把你所有的 代码到__init__.py。只做一个 模块而不是包,它是 更简单。
    • 试着想出 使 Python 能够 导入您的模块或包 让用户添加目录 将其包含在其导入路径中 (通过PYTHONPATH 或其他 机制)。你不会正确 处理所有案件,用户将获得 当你的软件生你的气 在他们的环境中不起作用。

    【讨论】:

      【解决方案2】:

      如果 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 文件。在这种情况下,解决方案是不这样做。 :-)

      【讨论】:

      • hmmm 实际上不是这样,我会看看如何用 main.py 外部包复制它
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-09-21
      • 1970-01-01
      • 1970-01-01
      • 2015-12-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多