Google

FEB

26TH, 2015

Python相对导入机制详解

这个答案能解释大多关于 relative import,即相对导入的疑惑,讲解十分详尽清晰,算是 SO 上被低估的一个答案。

问题不翻译了,直接摘录下来:

The forever-recurring question is this: With Windows 7, 32-bit Python 2.7.3, how do I solve this "Attempted relative import in non-package" message? I built an exact replica of the package on pep-0328:

package/

__init__.py

subpackage1/

__init__.py

moduleX.py

moduleY.py

subpackage2/

__init__.py

moduleZ.py

moduleA.py

I did make functions named spam and eggs in their appropriate modules. Naturally, it didn't work. The answer is apparently in the 4th URL I listed, but it's all alumni to me. There was this response on one of the URLs I visited:

Relative imports use a module's name attribute to determine that module's position in the package hierarchy. If the module's name does not contain any package information (e.g. it is set to'main') then relative imports are resolved as if the module were a top level module, regardless of where the module is actually located on the file system.

The above response looks promising, but it's all hieroglyphs to me. So my question, how do I make Python not return to me"Attempted relative import in non-package"? has an answer that involves -m, supposedly.

Can somebody please tell me why Python gives that error message, what it Means by non-package!, why and how do you define a 'package', and the precise answer put in terms easy enough for a kindergartener to understand. Thanks in advance!

Edit: The imports were done from the console.

BrenBarn 的精彩答案(这个哥们可以算是 import 专家了,答了好多这方面的题)

简单地说,直接运行 .py 文件和 import 这个文件有很大区别。Python 解释器判断一个 py 文件属于哪个 package 时并不完全由该文件所在的文件夹决定。它还取决于这个文件是如何 load 进来的(直接运行 or import)。

有两种方式加载一个 py 文件:作为 top-level 脚本或者作为 module。前者指的是直接运行脚本,比如 python myfile.py。如果执行 python -m myfile,或者在其它 py 文件中用 import 语句来加载,那么它就会被当作一个 module。有且只能有一个 top-level 脚本,就是最开始执行的那个(比如 python myfile.py 中的 myfile.py,译者注)。

当一个 py 文件被加载之后,它会被赋予一个名字,保存在 __name__ 属性中。如果是 top-level 脚本,那么名字就是 __main__。如果是作为 module,名字就是把它所在的 packages/subpackages 和文件名用 . 连接起来。

例如,moduleX 被 import 进来,它的名字就是 package.subpackage1.moduleX。如果 import 了 moduleA,它的名字是 package.moduleA。如果直接运行 moduleX 或 moduleA,那么名字就都是 __main__ 了。

另一个令人担忧的问题是,一个 module 的名字取决于它是直接从它所在的文件夹 import 还是通过某个 package import 的。不过只有当你在某个路径中运行 Python 并试图从当前文件夹 import 一个 py 文件时,才需要关注它们的不同。例如,在路径 pacakge/subpackage1 中运行 python 解释器,然后脚本中有 import moduleX 这个语句,此时 moduleX 的名字正是 moduleX,而不是 package.subpackage1.moduleX。这是因为 Python 解释器在启动时把当前路径(这里答案写的不准确,其实加入的是 top-level 脚本的路径,因为两者在这种状况下相同,所以也并不算错。译者注)加入了它的搜索路径 (sys.path);如果发现要 import 的 module 就在当前路径,那么 Python 解释器就不知道当前路径是属于哪个 package 的,所以 pacakge 的信息就不会成为 module 的名字的一部分。

一个特例是直接运行 python REPL,这个 REPL 的 session 的名字是 __main__。

关于你遇到的错误信息,关键点来了:如果一个 module 的名字中没有点(即 package.subpackage1 中的那个点,译者注),那么它就被认为不属于任何一个 package。文件在磁盘上的位置在哪里都不影响,唯一起决定作用的就是 module 的名字,而这又取决于它是如何被加载的。

先在我们看看你在问题中引用的这段话

Relative imports use a module's name attribute to determine that module's position in the package hierarchy. If the module's name does not contain any package information (e.g. it is set to'main') then relative imports are resolved as if the module were a top level module, regardless of where the module is actually located on the file system.

relative import 使用 module 的名字来决定它是否属于一个 package,属于哪个 package。当你使用这种 relative import from .. import foo,其中的点的数量代表了 package 结构中的某个层次。例如,如果当前 module 的名字是 package.subpackage1.moduleX,那么 ..moduleA 代表 package.moduleA。为了让形如 from .. import 的这种导入能够正常工作,module 的名字里的点数量应当至少和 import 语句中一样多。

前面说了,如果 module 的名字是 __main__,那么 Python 就不认为它属于某个 package。由于名字里不包含点,所以在这个 py 文件中 from .. import 语句无法正常工作。试图执行这条语句就会报 "relative-import in non-package" 错误。

你犯的错误可能是从命令行运行 moduleX 或类似的操作。当你执行这个操作,moduleX 的名字被设置成 __main__,所以 relative imports 失败了,因为不包含 package 信息。正如前面说的,如果在同一个路径里 import 一个文件,这时 module 的名字就是文件名,不包含 package 信息,所以相对导入也会失败。

记住,因为 REPL session 的名字总是 __main__,所以试图在 REPL 里执行 relative import 是不行的。relative import 应当只在 module 文件中被使用。

(无法相对导入的问题)有两个解决方法。如果你真的想直接运行 moduleX,同时又希望它被当作所在 package 的一部分,可以这么做:python -m package.subpackage.moduleX。-m 参数告诉 Python 解释器,把这个文件当作一个 module 载入,而不是 top-level 脚本。

如果你并不想直接运行 moduleX,而是想在另一个文件比如 myfile.py 中使用 moduleX 中定义的函数,那么解决方法是把 myfile.py 文件挪到另一个地方,只要不在 moduleX 所属的 package 的文件夹里就行。然后在 myfile.py 中执行 from package.moduleA import spam,就能正常工作了。

注意,不论哪种解决方案,都需要 package 的路径(上文中的 package)在 python 的搜索路径也就是 sys.path 里。如果不在,那么就无法使用这个 package 中的任何文件。

(更严谨的说明:从 Python2.6 开始,在做 package-resolution 时,module 的 “名字” 并不完全等于 __name__ 属性,还和 __package__ 属性有关。这也是为什么上文中我一直尽量避免用 __name__ 来指代 module 的名字。从 python2.6 开始,一个 module 的 “名字” 实际上是 __package__ + '.' + __name__, 或者直接就是 __name__,如果 __package__ 是 None 的话)

comments powered by Disqus

python 相对导入_Python相对导入机制详解相关推荐

  1. python输出命令_Python输出各行命令详解

    创建main.py文件并粘贴下面代码 点击右键运行Debug 'main'后,下方的Debug窗口会出现ImportError: No module named 'bottle'这样的提示,提示导入b ...

  2. python md5加密_Python MD5加密实例详解

    详解Python MD5加密 Python 3下MD5加密 # 由于MD5模块在python3中被移除 # 在python3中使用hashlib模块进行md5操作 import hashlib # 待 ...

  3. 简述python文件操作_Python 文件操作的详解及实例

    Python 文件操作的详解及实例 一.文件操作 1.对文件操作流程 打开文件,得到文件句柄并赋值给一个变量 通过句柄对文件进行操作 关闭文件 现有文件如下: 昨夜寒蛩不住鸣. 惊回千里梦,已三更. ...

  4. python 类 实例_Python类的实例详解

    类(class)是一个用户自定义类型,开发者可以将其实例化以获得实例(instance),实例表示这种类型的对象.在Python中,类就是对象,开发者可以像对其他对象那样处理函数,可以在调用函数时传递 ...

  5. python pdb 安装_Python调试工具pdb使用详解

    Python调试工具pdb使用详解 [简介] pdb是Python自带的一个包,为python程序提供了一种交互的源代码调试功能. [使用方法] 1. 使用命令: python -m pdb xxx. ...

  6. python垃圾回收价格表_Python垃圾回收机制详解

    一.垃圾回收机制 Python中的垃圾回收是以引用计数为主,分代收集为辅.引用计数的缺陷是循环引用的问题. 在Python中,如果一个对象的引用数为0,Python虚拟机就会回收这个对象的内存. #e ...

  7. python init文件_Python __init__.py 作用详解

    __init__.py 文件的作用是将文件夹变为一个Python模块,Python 中的每个模块的包中,都有__init__.py 文件. 通常__init__.py 文件为空,但是我们还可以为它增加 ...

  8. python random函数_Python随机函数random使用详解

    在python中用于生成随机数的模块是random,在使用前需要import, 下面看下它的用法. 1.random.random random.random()用于生成一个0到1的随机符点数: 0 ...

  9. python数组越界_python 整数越界问题详解

    python 内部自带大整数运算能力,整数运算不会溢出,只要内存足够,就oK 下面的例子演示了两个32位整数加法的情况(通过位运算实现),为了模拟溢出的效果,必须人工的进行位运算,~运算符除了求反,还 ...

  10. python 传感器数据结构_Python常用的数据结构详解

    数据结构:通俗点说,就是储存大量数据的容器.这里主要介绍Python的4种基本数据结构:列表.字典.元组.集合. 格式如下: 列表:list = [val1,val2,val3,val4],用中括号: ...

最新文章

  1. SwiftUI 发展现状和学习指南
  2. 洛谷 P1028 数的计算
  3. centos7启动与切换图形界面
  4. 给git配置http代理
  5. html4的语法,HTML——语法
  6. java 创建ssh用户秘钥,安装Java、Maven、Git,以及生成、拷贝密钥
  7. Python爬取B站5000条视频,揭秘为何千万人看「哪吒」流泪
  8. kalman filter java_Kalman filters(一)
  9. layUI表单验证不生效的问题
  10. 【BZOJ1116】[POI2008]CLO 并查集
  11. 围棋三番棋,得第二局得胜
  12. 常用滤波算法(转载)
  13. HTML5期末大作业:汽车销售网站模板设计(7个页面) HTML+CSS+JavaScript 企业网页设计源码...
  14. python word文档合并_[Python 学习笔记]用Python进行docx文档合并
  15. DSP开发的一点概念
  16. 独家 | 零基础入门优化问题
  17. 安卓毕业设计选题基于Uniapp实现的Android的校园二手商品交易平台
  18. 解析自动休眠---实现自动关机
  19. 如何查看路由器的宽带连接密码
  20. 当RxJava遇上Retrofit

热门文章

  1. 第二次作业——Python基础和软件工程
  2. guava 对集合的支持
  3. 51Nod 1873 - 初中的算术(JAVA)
  4. 14.链表中倒数第k个结点
  5. iOS 快捷下载和安装并使用CocoaPods
  6. scala中的数组的转换操作
  7. Magento 获取分类的父分类和子分类
  8. SUS安装配置简明图解攻略
  9. [转载] [556]python实现神经网络
  10. 生成随机验证码,上传图片文件,解析HTML