繁体   English   中英

Python 2中的绝对vs相对导入

[英]Absolute vs Relative Imports in Python 2

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

这是一个例子:

文件结构:

\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

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

http://docs.python.org/2/whatsnew/2.5.html#pep-328-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(string)查看要导入的字符串模块


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导入机制相对于当前文件的名称起作用。 直接执行文件时,它没有通常的名称,而是使用“main”作为其名称。 所以相对进口不起作用。 正如Igancio建议的那样,您可以使用-m选项执行它。 如果您的软件包的一部分意味着作为脚本运行,您还可以使用package属性来告诉该文件它应该在软件包层次结构中具有什么名称。 有关详细信息,请访问http://www.python.org/dev/peps/pep-0366/

绝对/相对导入是打包。

在Python 2.x(现在是Python 2.7.x)中,默认导入功能是隐式相对导入。

如上所示,它将首先导入包下的同名模块。 使用绝对导入作为默认值,Python将仅通过sys.path序列导入。

如果要使用相对导入,则必须使用显式相对导入

那个作为导入列表:

显式优于隐式

参考文献:

我相信这可以通过操作python搜索模块的目录的sys.path列表来解决。 添加当前目录'。' 在导入模块之前甚至os.getcwd()可能对你os.getcwd()

像这样的东西:

import sys,os
sys.path.append(os.getcwd())

from mymodule import MyClass

...

如果您需要有关python源代码的位置或脚本运行位置的更多信息,请查看inpect python模块

像这样运行它。

python -m test_package.sub_package_b.module_b

欲获得更多信息,

如何修复“非包装中的尝试相对导入”,即使使用__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

要进行这些不同类型的导入,必须将<root>/<root>/test_package/放入sys.path ,从每个要执行的python文件中。 所以主要是:

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM