模块重新导入如果来自不同路径的进口(module reimported if imported fr

2019-07-18 23:25发布

在一个大的应用程序,我的工作,几个人导入相同的模块不同如进口X或Y的进口X的的副作用为x导入两次,可能会引入非常微妙的错误,如果有人依靠全局属性

例如,假设我有三个文件mymodule.py,main.py和初始化的.py包mypakcage

mymodule.py内容

l = []
class A(object): pass

main.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())

题:

有什么办法避免这种情况? 除了强制执行该模块应导入一种方式磺酰基。 不能在此通过蟒蛇进口机制来处理,我已经看到了Django的代码与此相关的几个bug和其他地方了。

Answer 1:

如果main.py是你实际运行的文件,我只能重复这一点。 在这种情况下,你会得到main.py的当前目录中的SYS路径上。 但你显然也有一个系统路径设置,使得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'>

但另增加mainfile,在当期的目录:

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



Answer 2:

每个模块命名空间是进口的只有一次。 问题是,你在不同的导入他们。 在第一你从全局包导入,并在第二个你做一个地方的,无包装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的是如何(不)组织你的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解释和安装软件,分别,如果你感觉不错。

不要

  • 把你的源目录中名为srclib 。 这使得它很难在不安装运行。
  • 把你的测试你的Python包之外。 这使得它很难对运行安装版本的测试。
  • 创建一个包,只有一个__init__.py ,然后把所有的代码放到__init__.py 。 只是做一个模块,而不是一个包,它更简单。
  • 试着拿出神奇的黑客,才能使Python能够导入您的模块或包,而无需用户(通过添加它包含到他们的进口路径的目录PYTHONPATH或其他一些机制)。 您将不能正确地处理所有的情况下,用户将得到在你生气时,你的软件不能在他们的环境中工作。


文章来源: module reimported if imported from different path