在一个大的应用程序,我的工作,几个人导入相同的模块不同如进口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和其他地方了。
如果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文件。 而在这种情况下,解决方案是不能这样做。 :-)
每个模块命名空间是进口的只有一次。 问题是,你在不同的导入他们。 在第一你从全局包导入,并在第二个你做一个地方的,无包装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
解释和安装软件,分别,如果你感觉不错。
不要 :
- 把你的源目录中名为
src
或lib
。 这使得它很难在不安装运行。 - 把你的测试你的Python包之外。 这使得它很难对运行安装版本的测试。
- 创建一个包,只有一个
__init__.py
,然后把所有的代码放到__init__.py
。 只是做一个模块,而不是一个包,它更简单。 - 试着拿出神奇的黑客,才能使Python能够导入您的模块或包,而无需用户(通过添加它包含到他们的进口路径的目录
PYTHONPATH
或其他一些机制)。 您将不能正确地处理所有的情况下,用户将得到在你生气时,你的软件不能在他们的环境中工作。