Python 2中的绝对vs相对导入

时间:2015-02-15 15:43:22

标签: python-2.7

我想要的是一种从包中的相邻模块导入类的方法,无论我是否调用模块目录或将模块导入另一个模块。我无法想办法做这种导入。

以下是一个例子:

文件结构:

\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

我想要一种方法来执行适用于两种运行模式的导入。

4 个答案:

答案 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