在我工作的一个大型应用程序中,几个人以不同的方式导入相同的模块,例如 导入 x 或者 从 y 导入 x 其副作用是 x 被导入两次,如果有人依赖全局属性,可能会引入非常微妙的错误
例如假设我有一个包含三个文件 mymodule.py、main.py 和 init.py
的包 mypakcagemymodule.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是不同的 对我来说,这看起来很严重,因为类本身将受到不同的对待 例如下面的代码打印 False
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 代码和其他地方也看到了几个与此相关的错误。
最佳答案
每个模块命名空间只导入一次。问题是,您以不同的方式导入它们。第一个是从全局包导入,第二个是本地非打包 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
我找到了 this document由 Jp Calderone 撰写,是关于如何(不)构建 Python 项目的重要提示。按照它你不会有问题。注意 bin
文件夹 - 它在包外。我将在此处复制整个文本:
Filesystem structure of a Python project
Do:
- name the directory something related to your project. For example, if your project is named "Twisted", name the top-level directory for its source files
Twisted
. When you do releases, you should include a version number suffix:Twisted-2.5
.- create a directory
Twisted/bin
and put your executables there, if you have any. Don't give them a.py
extension, even if they are Python source files. Don't put any code in them except an import of and call to a main function defined somewhere else in your projects.- If your project is expressable as a single Python source file, then put it into the directory and name it something related to your project. For example,
Twisted/twisted.py
. If you need multiple source files, create a package instead (Twisted/twisted/
, with an emptyTwisted/twisted/__init__.py
) and place your source files in it. For example,Twisted/twisted/internet.py
.- put your unit tests in a sub-package of your package (note - this means that the single Python source file option above was a trick - you always need at least one other file for your unit tests). For example,
Twisted/twisted/test/
. Of course, make it a package withTwisted/twisted/test/__init__.py
. Place tests in files likeTwisted/twisted/test/test_internet.py
.- add
Twisted/README
and Twisted/setup.py
to explain and install your software, respectively, if you're feeling nice.Don't:
- put your source in a directory called
src
orlib
. This makes it hard to run without installing.- put your tests outside of your Python package. This makes it hard to run the tests against an installed version.
- create a package that only has a
__init__.py
and then put all your code into__init__.py
. Just make a module instead of a package, it's simpler.- try to come up with magical hacks to make Python able to import your module or package without having the user add the directory containing it to their import path (either via
PYTHONPATH
or some other mechanism). You will not correctly handle all cases and users will get angry at you when your software doesn't work in their environment.
关于python - 如果从不同的路径导入模块重新导入,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1459236/