我想要的是一种从包中的相邻模块导入类的方法,无论我是否调用模块目录或将模块导入另一个模块。我无法想办法做这种导入。
以下是一个例子:
文件结构:
\test_package
\sub_package_a
__init__.py
module_a.py
\sub_package_b
__init__.py
module_b.py
__init__.py
main.py
main.py:
from sub_package_b.module_b import ClassInModuleB
b = ClassInModuleB()
module_a.py:
class ClassInModuleA(object):
pass
module_b.py:
# I need a class in module a, this seems the most natural way to me
try:
from test_package.sub_package_a.module_a import ClassInModuleA
except ImportError:
print "Could not import using absolute path"
else:
print "Imported using absolute path"
# This works, but only if importing moudle, not if running it as a script
try:
from sub_package_a.module_a import ClassInModuleA
except ImportError:
print "Could not import using relative path"
else:
print "Imported using relative path"
class ClassInModuleB(object):
pass
以下是我观察到的令我困惑的事情:
> python test_package\main.py
Could not import using absolute path
Imported using relative path
> python test_package\sub_package_b\module_b.py
Could not import using absolute path
Could not import using relative path
我想要一种方法来执行适用于两种运行模式的导入。
答案 0 :(得分:2)
http://docs.python.org/2/whatsnew/2.5.html#pep-328-absolute-and-relative-imports:
Absolute and Relative Imports解释非常详细。
absolute_import 功能在Python 3.x中是默认的。 (我使用Python 2.7.x)
使用示例:
pkg
├── __init__.py
├── main.py
└── string.py
The content of string.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
def say_hello():
print "say hello"
第一版main.py的内容:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import string
string.say_hello()
# move to the parent dir of pkg
$ python -m pkg.main
say hello
这将使用相对的string.py模块,而不是Python的标准字符串模块。
使用absolute_import时:
来自Python手册:
绝对导入是默认导入后,将始终找到导入字符串 标准库的版本。建议用户应该开始 尽可能使用绝对导入,所以最好是 从代码中的pkg import string开始编写。
from __future__ import absolute_import
#import string # This is error because `import string` will use the standard string module
from pkg import string
string.say_hello()
通过向领先期间添加一个领先期,仍然可以进行相对进口 使用from ... import表单时的模块名称:
from __future__ import absolute_import
from . import string # This is the same as `from pkg import string`
string.say_hello()
或
from __future__ import absolute_import
from .string import say_hello
say_hello()
使用 print(字符串)查看要导入的字符串模块
<强> main.py:强>
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import absolute_import
import string
print(string)
string.say_hello()
如果按以下方式运行代码:
cd pkg
$ python pkg/main.py
<module 'string' from '/path/to/my/pkg/string.pyc'>
say hello
它将始终使用本地string.py,因为当前路径是sys.path
中的第一个路径将main.py更改为:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from . import string
print(string)
string.say_hello()
运行代码:
cd pkg
$ python pkg/main.py
Traceback (most recent call last):
File "pkg/main.py", line 3, in <module>
from . import string
ValueError: Attempted relative import in non-package
这个答案很详细: https://stackoverflow.com/a/11537218/1276501
详细说明@ Ignacio的答案:python导入机制有效 相对于当前文件的名称。执行文件时 直接,它没有它的通常名称,但有“主”作为其名称 代替。所以相对进口不起作用。你可以像Igancio一样 建议,使用-m选项执行它。如果你有自己的一部分 这个包意味着要作为脚本运行,你也可以使用 package属性告诉该文件它应该具有什么名称 包层次结构。见http://www.python.org/dev/peps/pep-0366/ 详情。
绝对/相对导入是打包。
在Python 2.x(现在是Python 2.7.x)中,默认导入功能是隐式相对导入。
如上所述,它将首先导入包下的同名模块。使用绝对导入作为默认值,Python将仅通过sys.path序列导入。
如果您想使用相对导入,则必须使用显式相对导入
这是导入中的列表:
显式优于隐式
<强>参考文献:强>
答案 1 :(得分:0)
我相信这可以通过操作python搜索模块的sys.path目录列表来解决。添加当前目录'。'在导入模块之前甚至os.getcwd()
可能对您有用。
这样的事情:
import sys,os
sys.path.append(os.getcwd())
from mymodule import MyClass
...
如果您需要有关python源代码的位置或运行脚本的位置的更多信息,请查看inpect python module。
答案 2 :(得分:0)
像这样运行。
python -m test_package.sub_package_b.module_b
了解更多信息,
How to fix "Attempted relative import in non-package" even with __init__.py
和
https://www.python.org/dev/peps/pep-0366/
要相对导入,
替换
sub_package_a.module_a import ClassInModuleA
与
from ..sub_package_a.module_a import ClassInModuleA
答案 3 :(得分:0)
要进行这些不同类型的导入,您必须从要执行的每个python文件中将<root>/
和<root>/test_package/
放入sys.path
。所以主要是:
import os
import sys
import inspect
# Get the current folder, which is the input folder
current_folder = os.path.realpath(
os.path.abspath(
os.path.split(
inspect.getfile(
inspect.currentframe()
)
)[0]
)
)
folder_parts = current_folder.split(os.sep)
previous_folder = os.sep.join(folder_parts[0:-1])
sys.path.insert(0, current_folder)
sys.path.insert(0, previous_folder)
# Rest of main
在模块B中它将是:
import os
import sys
import inspect
from pprint import pprint
# Get the current folder, which is the input folder
mod_b_folder = os.path.realpath(
os.path.abspath(
os.path.split(
inspect.getfile(
inspect.currentframe()
)
)[0]
)
)
folder_parts = mod_b_folder.split(os.sep)
prev_test_pack_path = os.sep.join(folder_parts[0:-2])
test_pack_path = os.sep.join(folder_parts[0:-1])
sys.path.insert(0, test_pack_path)
sys.path.insert(0, prev_test_pack_path)
# Rest of module B
但是,我建议使用一个命名系统导入模块,并将相应的文件夹插入sys.path
。