Relative imports for the billionth time
我来过这里:
- http://www.python.org/dev/peps/pep-0328/
- http://docs.python.org/2/tutorial/modules.html包
- python包:相对导入
- python相对导入示例代码不起作用
- 相对python导入的最终答案
- python中的相对导入
- python:禁用相对导入
还有很多我没有复制的网址,一些在上面,一些在其他网站上,当我认为我可以很快得到解决方案的时候。
永远重复出现的问题是:对于Windows7,32位python 2.7.3,如何解决这个"尝试在非包中相对导入"的消息?我在PEP-0328上构建了一个包的精确副本:
1 2 3 4 5 6 7 8 9 10 | package/ __init__.py subpackage1/ __init__.py moduleX.py moduleY.py subpackage2/ __init__.py moduleZ.py moduleA.py |
我确实在相应的模块中创建了名为spam和eggs的函数。当然,它不起作用。答案显然在我列出的第四个网址上,但都是我的校友。我访问的其中一个URL上有此响应:
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.
上面的回答看起来很有希望,但对我来说都是象形文字。所以我的问题是,如何使python不返回到我的"尝试在非包中进行相对导入"?有一个答案,可能涉及-m。
有人能告诉我为什么python会给出这个错误消息,非包意味着什么吗?,为什么和如何定义一个"包裹",以及准确的答案,用足够简单的术语来表达,让幼儿园学生能够理解。
编辑:从控制台导入。
脚本与模块好的。
这是一个解释。简短的版本是,直接运行一个python文件和从其他地方导入该文件有很大的区别。仅仅知道文件在哪个目录中并不能确定python认为它在哪个包中。另外,这取决于如何将文件加载到Python中(通过运行或导入)。好的。
加载python文件有两种方法:作为顶级脚本,或作为模块。如果直接执行文件,例如通过在命令行上键入
命名好的。
加载文件时,会为其指定一个名称(存储在其
例如,在您的示例中:好的。
1 2 3 4 5 6 | package/ __init__.py subpackage1/ __init__.py moduleX.py moduleA.py |
如果您导入
不通过模块的包含包访问模块好的。
还有一个额外的问题:模块的名称取决于它是从它所在的目录"直接"导入的,还是通过包导入的。如果您在一个目录中运行python,并尝试在同一个目录(或它的子目录)中导入一个文件,那么这只会有所不同。例如,如果启动目录
一个特殊的情况是,如果您以交互方式运行解释器(例如,只需键入
现在,对于您的错误消息来说,关键是:如果一个模块的名称没有点,那么它就不被认为是包的一部分。文件在磁盘上的实际位置并不重要。重要的是它的名称是什么,它的名称取决于您如何加载它。好的。
现在看看你的问题中包含的报价:好的。
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.
Ok.
相对导入…好的。
相对导入使用模块的名称来确定它在包中的位置。当您使用相对导入(如
…仅在包中是相对的好的。
但是,如果您的模块名是
脚本无法导入相对好的。
您可能会尝试从命令行运行
还要记住,当您运行交互式解释器时,该交互式会话的"名称"始终是
两种解决方案:好的。
如果您确实想直接运行
或者你可能不想运行
笔记好的。
对于这些解决方案中的任何一个,包目录(示例中的
package )都必须可以从python模块搜索路径(sys.path 访问。否则,您将无法可靠地使用包中的任何内容。好的。由于python 2.6,用于包解析的模块的"名称"不仅由其
__name__ 属性决定,而且由__package__ 属性决定。这就是为什么我避免使用显式符号__name__ 来引用模块的"名称"。因为python 2.6模块的"名称"实际上是__package__ + '.' + __name__ ,或者如果__package__ 是None ,则仅是__name__ 。好的。
好啊。
这确实是Python中的一个问题。困惑的根源在于人们错误地把相对进口视为路径相对,而不是路径相对。
例如,在faa.py中编写时:
1 | from .. import foo |
只有当faa.py在执行期间被python标识和加载为包的一部分时,这才有意义。在这种情况下,模块的名称例如,对于faa.py,可能是一些_PackageName.faa。如果只因为文件在当前目录中而加载了该文件,那么在运行python时,它的名称将不会引用任何包,最终相对导入将失败。
在当前目录中引用模块的一个简单解决方案是使用:
1 2 3 4 5 6 | if __package__ is None or __package__ == '': # uses current directory visibility import foo else: # uses current package visibility from . import foo |
下面是一个通用的方法,作为一个例子进行了修改,我现在使用它来处理作为包编写的Python库,这些库包含相互依赖的文件,我希望能够在其中逐段测试其中的某些部分。我们称之为
我打了几通
这个特殊的例子太简单了,无法显示何时真正需要在
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | from __future__ import print_function # only when showing how this works if __package__: print('Package named {!r}; __name__ is {!r}'.format(__package__, __name__)) from .fileA import f1, f2 from .fileB import Class3 else: print('Not a package; __name__ is {!r}'.format(__name__)) # these next steps should be used only with care and if needed # (remove the sys.path manipulation for simple cases!) import os, sys _i = os.path.dirname(os.path.abspath(__file__)) if _i not in sys.path: print('inserting {!r} into sys.path'.format(_i)) sys.path.insert(0, _i) else: print('{!r} is already in sys.path'.format(_i)) del _i # clean up global name space from fileA import f1, f2 from fileB import Class3 ... all the code as usual ... if __name__ == '__main__': import doctest, sys ret = doctest.testmod() sys.exit(0 if ret.failed == 0 else 1) |
这里的想法是这样的(请注意,这些功能在python2.7和python 3.x中都是相同的):
如果作为从普通代码导入的常规包运行
如果按
我们采用第二条代码路径。
如果在
如果在
(由于
笔记
还有一个怪癖。如果你从外面把这整件事处理掉:
1 | $ python2 lib.foo |
或:
1 | $ python3 lib.foo |
行为取决于
1 | Package named 'lib'; __name__ is '__main__' |
但是如果
1 2 3 | $ python2 lib.foo Package named 'lib'; __name__ is 'lib.foo' Package named 'lib'; __name__ is '__main__' |
也就是说,模块被导入两次,一次通过包,然后再次作为
1 2 3 4 5 6 7 | $ python3 lib.routine Package named 'lib'; __name__ is 'lib.foo' [...]/runpy.py:125: RuntimeWarning: 'lib.foo' found in sys.modules after import of package 'lib', but prior to execution of 'lib.foo'; this may result in unpredictable behaviour warn(RuntimeWarning(msg)) Package named 'lib'; __name__ is '__main__' |
警告是新的,但关于行为的警告不是。它是一些人称之为双重导入陷阱的一部分。(更多细节见27487期)尼克·科格伦说:
This next trap exists in all current versions of Python, including 3.3, and can be summed up in the following general guideline:"Never add a package directory, or any directory inside a package, directly to the Python path".
请注意,虽然我们在这里违反了这个规则,但是只有当正在加载的文件不是作为包的一部分加载时,我们才这样做,并且我们的修改是专门为允许我们访问该包中的其他文件而设计的。(而且,正如我所指出的,我们可能根本不应该对单个级别的包执行此操作。)如果我们想要更加干净,我们可以将其重写为,例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 | import os, sys _i = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) if _i not in sys.path: sys.path.insert(0, _i) else: _i = None from sub.fileA import f1, f2 from sub.fileB import Class3 if _i: sys.path.remove(_i) del _i |
也就是说,我们修改
因此,在与许多其他人讨论了这一点之后,我看到了Dorian B在本文中发布的一条注释,它解决了我在开发用于Web服务的模块和类时遇到的特定问题,但是我也希望能够在编码时使用pycharm中的调试器工具对它们进行测试。要在独立类中运行测试,我将在类文件的末尾包含以下内容:
1 2 | if __name__ == '__main__': # run test code here... |
但是,如果我想导入同一文件夹中的其他类或模块,那么我将不得不将所有导入语句从相对符号更改为本地引用(即删除点(.),但是在阅读了Dorian的建议之后,我尝试了他的"一行程序",结果成功了!我现在可以在pycharm中进行测试,当我在另一个正在测试的类中使用该类时,或者当我在Web服务中使用该类时,将测试代码保留在适当的位置!
1 2 3 4 5 6 7 8 9 | # import any site-lib modules first, then... import sys parent_module = sys.modules['.'.join(__name__.split('.')[:-1]) or '__main__'] if __name__ == '__main__' or parent_module.__name__ == '__main__': from codex import Codex # these are in same folder as module under test! from dblogger import DbLogger else: from .codex import Codex from .dblogger import DbLogger |
if语句检查我们是否将此模块作为主模块运行,或者它是否正在另一个作为主模块进行测试的模块中使用。也许这是显而易见的,但我在这里提供了这个注释,以防其他任何人对上面的相关进口问题感到沮丧,可以利用它。
这里是一个解决方案,我将不推荐,但可能是有用的在一些情况下是简单的:在不生成模块
1 2 3 4 5 6 | import os import sys parent_dir_name = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) sys.path.append(parent_dir_name +"/your_dir") import your_script your_script.a_function() |
如果代码没有运行在全球名称空间,将
你会看到很多Python代码是用来测试是否与
你试着从这些进口的控制台?
我有一个类似的问题Where I didn’t want to change the Python模块搜索所需的路径和负载模块的比较(尽管从脚本"脚本中的所有不相关的进出brenbarn解释说,"nicely以上)。
我使用下面的破解。不幸的是,它是在
的例子是访问的成员从外
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | #!/usr/bin/env python3 import inspect import imp import os def get_script_dir(follow_symlinks=True): """ Return directory of code defining this very function. Should work from a module as well as from a script. """ script_path = inspect.getabsfile(get_script_dir) if follow_symlinks: script_path = os.path.realpath(script_path) return os.path.dirname(script_path) # loading the module (hack, relying on deprecated imp-module) PARENT_PATH = os.path.dirname(get_script_dir()) (x_file, x_path, x_desc) = imp.find_module('moduleX', [PARENT_PATH+'/'+'subpackage1']) module_x = imp.load_module('subpackage1.moduleX', x_file, x_path, x_desc) # importing a function and a value function = module_x.my_function VALUE = module_x.MY_CONST |
清洁方法似乎被修改用于加载的模块包含在上述的对他。
1 2 3 4 5 6 7 8 | #!/usr/bin/env python3 if __name__ == '__main__' and __package__ is None: from os import sys, path # __file__ should be defined in this case PARENT_DIR = path.dirname(path.dirname(path.abspath(__file__))) sys.path.append(PARENT_DIR) from subpackage1.moduleX import * |
使python不返回"尝试在非包中进行相对导入"。包装/
英利子包1号/英利模块化模块化子包2/英利模块化模块化
只有在将相对导入应用于父文件时才会出现此错误。例如,在modulea.py中对"print(name)"进行编码之后,父文件已经返回了main。因此,该文件已经是main文件了,它不能再返回任何父包。包子包1和子包2的文件中需要相对导入您可以使用".."来引用父目录或模块。但是父目录如果已经是顶级包,就不能再高于父目录(包)。您将相对导入应用于父级的此类文件只能与绝对导入应用程序一起使用。如果在父包中使用绝对导入,那么不会出现任何错误,因为python知道谁在包的顶层,即使您的文件在子包中,因为python path的概念定义了项目的顶层。
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.
写了一个小的python包给pypi,它可以帮助查看这个问题。如果希望能够从包/项目中运行包含包含上层包的导入的python文件,而不直接在导入文件的目录中,那么包将充当解决方案。https://pypi.org/project/import-anywhere网站/