otu*_*tus 6 python python-2.x package python-import python-3.x
当我开发纯Python 2的包时,我可以使用普通import b
语法导入相对路径,而不关心导入文件是否在包中.这样做的好处是我可以if __name__ == "__main__":
通过执行文件来运行任何文件的块,并且所有导入都可以正常工作.
添加Python 3支持后,我不得不转向新的相对导入语法,2.7也支持from . import b
.但是,此语法仅适用于包内.直接直接执行文件不再有效:
Traceback (most recent call last):
File "./a.py", line 2, in <module>
from . import b
ValueError: Attempted relative import in non-package
Run Code Online (Sandbox Code Playgroud)
解决方法是通过从上层目录将其作为模块导入来调用该文件:
python -m foo.a
Run Code Online (Sandbox Code Playgroud)
但是,这会对工作目录提出一个要求,这会阻止您将输出传递给其他同样关心工作目录的程序.
有没有办法让你的蛋糕和它吃?即支持作为脚本运行和作为包的一部分导入,同时在Python 2和3中工作?
示例包结构:
foo/
foo/__init__.py
foo/a.py (imports b)
foo/b.py (imports c)
foo/c.py
Run Code Online (Sandbox Code Playgroud)
我希望以下两个都适用于(a,b,c)中的x:
import foo.x (in some file when foo/ is in path)
python[23] path/to/foo/x.py
Run Code Online (Sandbox Code Playgroud)
下面的评论提到__package__
根据PEP 366设置,但"如果脚本被移动到不同的包或子包,则需要手动更新样板."
更新:我试图让PEP 366解决方案正常工作,但无法弄明白.它说:
sys.path
需要操作的附加代码才能使直接执行工作而顶级包不可导入.
从非导入包执行文件时就是这种情况.那些额外的代码会是什么样的?
这是一个基于KronoS 的答案和评论的解决方案,无论路径或包名称如何,都允许模块使用相同的样板:
if __name__ == "__main__" and __package__ == None:
import importlib
import os.path
import sys
def _gen_path():
head, tail = os.path.split(os.path.realpath(__file__))
while head:
if not os.path.isfile(os.path.join(head, '__init__.py')):
yield head
return
head, tail = os.path.split(head)
yield tail
def _load_package():
path = list(_gen_path())
syspath = sys.path[:]
sys.path[:0] = [path.pop()]
package = '.'.join(reversed(path))
importlib.import_module(package)
sys.path = syspath
return package
__package__ = _load_package()
Run Code Online (Sandbox Code Playgroud)
它沿着文件路径向上遍历直到存在__init__.py
标记包的文件,然后导入模块的父包,并__package__
正确设置。之后,相对导入就可以from ..bar import baz
正常工作了。
太糟糕了,将这些函数放在自己的模块中会让您回到原点。另外,似乎没有一种 Python 2/3 可移植的方法来限制sys.path
更改对导入的影响,因此基目录中的任何内容都可能会隐藏与父包一起导入的任何模块或包中的绝对导入。
归档时间: |
|
查看次数: |
559 次 |
最近记录: |