python相对包导入报“Attempted relative import in non-package”错误

在python当中使用相对包导入有的时候是一件非常让人痛苦的事情,有的时候使用了相对包导入明明可以在运行,但是换了一种运行方式又不可以了。这篇文章就要深度的解决这个问题,在看的过程要不断的敲代码来练习,领会python的相对包导入。

这篇文章是从stackoverflow翻译过来的,

Relative imports for the billionth time

问题描述

为了解决这个问题,我搜索了一下网站,当然还有更多的网站

这个问题是这样的,在win7,32位的电脑上,运行python2.7.3,如何解决"Attempted relative import in non-package"问题。我根据pep-0328建立了一下的目录结构

package/

__init__.py

subpackage1/

__init__.py

moduleX.py

moduleY.py

subpackage2/

__init__.py

moduleZ.py

moduleA.py

并且按照这个目录当中的要求,建立了了spam和eggs函数。很显然,当我运行它的时候,他并没有运行成功。我列的第四个URL当中可能有一些答案,但是我还是理解不了。那个URL里面的信息是这样的:

相对导入使用模块的名称属性来决定模块在包层次结构中的位置,如果模块的名称不包含任何包信息(例如:被设置成‘main’),那么相对导入则被解析为最顶层的位置,不管这个时候这个模块实际上位于文件系统中的什么位置。

上面的答案看起来还是有点希望的,但是对于我来说还是有点理解不了。所以,我的问题是:如何让我运行的python程序不再返回 "Attempted relative import in non-package",同时解释一下-m选项。

有没有人能够告诉我python为什么会报这个错误,这里的‘non-package’是什么意思,为什么以及如何去定义一个‘package’。有没有人能够告诉我一些浅显易懂的答案,好让我这个刚入门的人能够理解一些。


回答:

脚本VS模块(script vs module)

直接运行一个文件和在别的文件中导入这个文件是有很大区别的,仅仅知道一个文件在目录中的位置并不意味着python程序就认为它在什么位置。这是由python用何种方式加载(运行或者导入.run or import)这个文件来决定的。

python有两种加载文件的方法:一种是作为顶层的脚本,另一种时当做模块。如果你直接执行这个程序,那么这个文件就被当做是顶层脚本来执行了,在命令行里面输入 python myfile.py 就是这个情况。如果你输入python -m myfile.py或者在其他的文件当中使用import来导入这个文件的时候,它就被当做模块来导入。在同一时间里,只有一个顶层脚本,顶层脚本是这样的概念:它是一个能够让你的程序从这里开始的python文件。

【文件是一种无区别的叫法,直接从这个文件运行,那么这个文件就叫做脚本,导入这个文件,那么这个文件就是模块。包:是一个目录,需要有__init__.py文件。模块是module,包是package】

命名(naming)

当一个文件被加载进来,它就有一个名称(这个名称存储在__name__属性当中)。如果这个文件被当做一个顶层脚本来进行加载,那么它的名字就是__main__【这就是为什么我们经常会发现这样的语句:if __name__ == "__main__"】。如果它被当做一个模块加载,那么它的名称就是文件名称,加上它所在的包名,以及所有的顶层的包名,这些名称中间是用点号隔开的。

比如下面的例子

package/

__init__.py

subpackage1/

__init__.py

moduleX.py

moduleA.py

比如你导入moduleX(注:导入,不是直接运行),它的名称就package.subpackage1.mouleX。如果你导入moduleA的时候,它的名称就是package.moudleA。但是,当你直接从命令行里面运行moduleA的时候,他的名称则被替换为__main__。如果你直接从命令行运行moduleA,它的名称也是__main__。当一个模块被当做一个顶层脚本来执行的时候,它原来的名称则会被__main__取代。

不通过包来访问一个模块

这里有一个额外的问题:模块的名称取决于它是从它所在的目录中直接导入的,还是通过包导入的。不过这种情况只会发生在你在一个目录当中运行python,并且试图导入这个目录当中的文件的时候。【注:包导入是这样的情况,别人封装好了一个包(含有__init__.py文件),你直接通过包名.模块名来使用这个模块】。

举个例子,如果你在package/subpackage1目录当中打开python解释器,然后输入import moduleX,那么moduleX的名称就是moduleX,而不是package.subpackage1.moduleX。这是因为python把当前的目录添加到了搜索路径上面。如果它发现被包含的模块在当前的目录当中,它将不知道该目录也是模块的一部分,包的信息不会出现在模块名称当中。

一种特殊的情况是这样的:当你直接运行python解释器(比如,在命令行里面输入python,然后进入python解释器,输入代码)。在这种情况下,这种交互式的终端的名称是__main__.

现在,你的问题有了一个关键性的答案:如果一个模块名称当中没有点号,那么它就不会被当做是一个包。不管这个文件在磁盘的什么位置。所有关键在于,它的名称是什么,而这个名称取决于你如何加载它。

那么现在看一下你在其他的URL当中的引用的这句话:

相对导入使用模块的名称属性来决定模块在包层次结构中的位置,如果模块的名称不包含任何包信息(例如:被设置成‘main’),那么相对导入则被解析为最顶层的位置,不管这个时候这个模块实际上位于文件系统中的什么位置。

相对导入...

相对导入使用模块的名称去决定它在一个包中的位置。当你使用了一个像这样的相对导入:from .. import foo,这里的点号表明在包的层次结构当中上升几个包层次。比如,现在模块的名称是package.subpackage1.moudleX,然后..moduleA中的两个点号表示的是上升两个包,到达package,然后package和moduleA结合,最终成为package.moduleA。要让from .. import正常工作,模块的名称中必须至少有和上面相同多的点号数目。

【 举个例子:模块名称:A.B.C.D

则from . import X 表示上升一层,然后和X结合,最终成为A.B.C.X

from .. import X 则是上升两层,到达A.B,然后和X结合 ,结果为A.B.X   】

...只能用在相对导入当使用

如果你的模块的名称是__main__,那么它就不被认为是在一个包当中,因为它的名称当中不含有点,所以你不能在它的里面使用from .. import。如果你使用了这个语句,那么程序就会报“relative-import in non-package"错误。

脚本不能包含相对导入:

当你直接运行moduleX或者是在命令行终端里运行程序的时候,这个时候模块的名称都是__main__,这就表明你不能使用相对导入。因为他们的名称表示他们并不在一个包当中。注:当你运行python的目录就是你模块所在的目录的时候,上面这种情况也会发生,这种情况下python过早的寻找当前目录的模块,并没有认为他们也是包的一部分。

当你运行交互式的解释器的时候,交互式进程的名称永远是__main__,因此你不能在交互式进程当中使用相对导入。相对导入只能在模块文件当中使用。

两个解决方法:

1:如果你想直接运行moduleX,但是你又想把它当做一个包的一部分,你可以使用python -m package.subpackage.moduleX. -m参数告诉python把它当做一个模块来加载,而不是顶层的脚本。

2:或许你并不想直接运行moduleX,你想在其它的脚本当中使用moduleX的函数,比如说这个脚本是myfile.py。如果是这种情况,需要把myfile.py放在别的地方,而不是在package目录里面。在myfile.py使用一下语句就可以正常工作了:from package.moduleA import spam.

注:对于上面说的这两种情况,包目录(比如上面的package)必须存在于python的搜索路径下面(sys.path)。如果不存在,你将不能够使用包中的任何东西。

自从python2.6,模块的名称不在决定使用__name__属性,而是使用__packege__属性。这就是为什么我避免使用__name__这么明确的名称来代表一个模块的名称。自从python2.6,一个模块的名称是由__package__+‘.‘+__name__来确定的,如果__packege__是None的话,那么这个名称就是__name__了。

  

时间: 2024-10-11 04:58:43

python相对包导入报“Attempted relative import in non-package”错误的相关文章

Python自定义包引入【新手必学】

前言本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理.作者:sys_song python中的Module是比较重要的概念.常见的情况是,事先写好一个.py文 件,在另一个文件中需要import时,将事先写好的.py文件拷贝 到当前目录,或者是在sys.path中增加事先写好的.py文件所在的目录,然后import.这样的做法,对于少数文件是可行的,但如果程序数目很 多,层级很复杂,就很吃力了.如果你刚学python不久,有问题

Python包的相对导入时出现“ ‘Parent module ' not loaded, cannot perform relative import”的解决方法

在练习Python中package的相对导入时,即 from . import XXX 或者 from .. import XXX 时会遇到这样两个错误: SystemError: Parent module '' not loaded, cannot perform relative import 和 ValueError: attempted relative import beyond top-level package 其实这两个错误的原因归根结底是一样的:在涉及到相对导入时,packa

浅谈 Python 的模块导入

浅谈 Python 的模块导入 本文不讨论 Python 的导入机制(底层实现细节),仅讨论模块与包,以及导入语句相关的概念.通常,导入模块都是使用如下语句: import ... import ... as ... from ... import ... from ... import ... as ... 一般情况下,使用以上语句导入模块已经够用的.但是在一些特殊场景中,可能还需要其他的导入方式.例如 Python 还提供了 __import__ 内建函数和 importlib 模块来实现动

引入工程报包导入异常:import javax.servlet.annotation.WebFilter;

引入工程报包导入异常:import javax.servlet.annotation.WebFilter; (2013-02-21 16:38:00)   分类: java 今天上午导入了一个项目,用的是tomcat7.0的,但是我自己是tomcat6.0的,结果项目导入就很郁闷的发现有的类打上了红叉叉,进去一看,import javax.servlet.annotation.WebFilter 不能引入,找不到类,发现是少导入了servlet-api 包的问题,然后就去网上下载了一个包,可是导

python之块包导入

一.模块 1.什么是模块 常见的场景:一个模块就是一个包含了python定义和声明的文件,文件名就是模块名字加上.py的后缀. 但其实import加载的模块分为四个通用类别: 1 使用python编写的代码(.py文件) 2 已被编译为共享库或DLL的C或C++扩展 3 包好一组模块的包 4 使用C编写并链接到python解释器的内置模块 2.为什么要使用模块 如果你退出python解释器然后重新进入,那么你之前定义的函数或者变量都将丢失,因此我们通常将程序写到文件中以便永久保存下来,需要时就通

python入门20 导入模块(引包)

import xx 导入xx模块,就是变量xx指向该模块,通过变量xx可访问模块内的所有功能 import xx.yy   与  from xx import yy 使用不一样:前者如果使用yy下的fun()需写成xx.yy.fun(), 后者只写yy.fun()即可. python包:目录下带有__init__.py文件的就是包 #coding:utf-8 #/usr/bin/python """ 2018-11-18 dinghanhua 引包 ""&

Windows下使用pip安装python包是报错-UnicodeDecodeError: 'ascii' codec can't decode byte 0xcb in position 0

先交待下开发环境: 操作系统:Windows 7 Python版本:2.7.9 Pip版本:6.1.1 其他环境忽略 在windows下使用pip下载python包,出现如下错误 [plain] view plain copy Collecting xxxxxx Exception: Traceback (most recent call last): File "D:\Python27\lib\site-packages\pip-6.0.8-py2.7.egg\pip\basecommand.

没有缺少包但是报 The import org.springframework.stereotype.Controller cannot be的解决方案

没有缺少包但是报  The import org.springframework.stereotype.Controller cannot be ssm框架的项目,从公司电脑导出再导入本人笔记本,一开始,所有包都已经导入, 但是Java resource开头还是有红色感叹号,buildpath也没有任何JAR包的缺失. 然后把原本4.1.4版本的jar包更新为4.3.1的包有些错误包就自然解决. 原文地址:https://www.cnblogs.com/CatsBlog/p/8995281.ht

Python 之 包的导入

Python包的导入 有时候,我们需要把包里面的所有模块全部一次导入,就可以直接导入包,然后不需要使用上一篇文章的方法进行一个一个模块导入. 将core模块下的login与test模块一次性导入 1.编写main.py 程序导入包 import os import sys # print(sys.path) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) #父目录 # print(BASE_DIR) /