Anu*_*yal 8 python python-import
在我正在工作的大型应用程序中,有几个人以不同的方式导入相同的模块,例如导入x或从导入x导致x的副作用被导入两次,如果有人依赖于全局属性,可能会引入非常微妙的错误
例如,假设我有一个包mypakcage,包含三个文件mymodule.py,main.py和init .py
mymodule.py内容
l = []
class A(object): pass
Run Code Online (Sandbox Code Playgroud)
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()
Run Code Online (Sandbox Code Playgroud)
它打印
updated list [1]
lets check []
updated list [1, 1]
lets check again []
Run Code Online (Sandbox Code Playgroud)
因为现在在两个不同的模块中有两个列表,类似的A类是不同的对我来说它看起来很严重因为类本身将被区别对待,例如下面的代码打印False
def create():
from mypackage import mymodule
return mymodule.A()
def check(a):
import mymodule
return isinstance(a, mymodule.A)
print check(create())
Run Code Online (Sandbox Code Playgroud)
题:
有什么方法可以避免这种情况吗?除了强制执行该模块应该以单向onyl方式导入.这不能由python导入机制处理,我在django代码和其他地方也看到了与此相关的几个错误.
每个模块命名空间只导入一次。问题是,您以不同的方式导入它们。第一个是从全局包导入,第二个是本地的非打包import. Python 将模块视为不同的。第一个导入在内部缓存为mypackage.mymodule,第二个为mymoduleonly。
解决此问题的一种方法是始终使用绝对导入。也就是说,始终为您的模块提供从顶级包开始的绝对导入路径:
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
Run Code Online (Sandbox Code Playgroud)
请记住,您的入口点(您运行的文件main.py)也应该在包之外。当您希望入口点代码位于包内时,通常您使用运行小脚本来代替。例子:
runme.py,外包装:
from mypackage.main import main
main()
Run Code Online (Sandbox Code Playgroud)
并在main.py您添加:
def main():
# your code
Run Code Online (Sandbox Code Playgroud)
我发现Jp Calderone 的这篇文档是关于如何(不)构建 Python 项目的一个很好的提示。跟着它,你不会有问题。注意bin文件夹 - 它在包外。我将在此处复制整个文本:
Python 项目的文件系统结构
做:
- 将目录命名为与您的项目相关的内容。例如,如果您的项目名为“ Twisted ”,则为其源文件命名顶级目录
Twisted。当您发布版本时,您应该包含一个版本号后缀:Twisted-2.5.- 创建一个目录
Twisted/bin并将您的可执行文件放在那里,如果有的话。不要给它们.py扩展名,即使它们是 Python 源文件。除了导入和调用在项目中其他地方定义的 main 函数之外,不要在其中放置任何代码。- 如果您的项目可表示为单个 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来解释和安装您的软件。不要:
- 将您的源代码放在名为
srcor的目录中lib。这使得不安装就很难运行。- 将您的测试放在 Python 包之外。这使得很难针对已安装的版本运行测试。
- 创建一个只有一个的包,
__init__.py然后将所有代码放入__init__.py. 只需制作一个模块而不是一个包,它更简单。- 尝试想出一些神奇的技巧,使 Python 能够导入您的模块或包,而无需用户将包含它的目录添加到他们的导入路径中(通过
PYTHONPATH或其他某种机制)。您将无法正确处理所有情况,并且当您的软件在他们的环境中不起作用时,用户会生您的气。
仅当 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'>
Run Code Online (Sandbox Code Playgroud)
但在当前目录中添加另一个主文件:
realmain.py:
from mypackage import main
Run Code Online (Sandbox Code Playgroud)
结果不同:
mypackage.mymodule path <module 'mypackage.mymodule' from '/tmp/mypackage/mymodule.pyc'>
mymodule path <module 'mypackage.mymodule' from '/tmp/mypackage/mymodule.pyc'>
Run Code Online (Sandbox Code Playgroud)
所以我怀疑你的主要 python 文件在包中。在这种情况下,解决方案就是不这样做。:-)
| 归档时间: |
|
| 查看次数: |
1219 次 |
| 最近记录: |