Python 3模块和程序包相对导入不起作用? [英] Python 3 modules and package relative import doesn't work?

查看:104
本文介绍了Python 3模块和程序包相对导入不起作用?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在构建项目结构时遇到一些困难.

I have some difficulties constructing my project structure.

这是我的项目目录结构:

This is my project directory structure :

MusicDownloader/
   __init__.py
   main.py
   util.py
   chart/
      __init__.py
      chart_crawler.py
   test/
      __init__.py
      test_chart_crawler.py

这些是代码:

1.main.py

1.main.py

from chart.chart_crawler import MelonChartCrawler

crawler = MelonChartCrawler()

2.test_chart_crawler.py

2.test_chart_crawler.py

from ..chart.chart_crawler import MelonChartCrawler

def test_melon_chart_crawler():
  crawler = MelonChartCrawler()

3.chart_crawler.py

3.chart_crawler.py

import sys
sys.path.append("/Users/Chois/Desktop/Programming/Project/WebScrap/MusicDownloader")
from .. import util

class MelonChartCrawler:
  def __init__(self):
    pass

4.util.py

4.util.py

def hi():
   print("hi")

在MusicDownloader中,当我通过python main.py执行main.py时,它显示错误:

In MusicDownloader, when I execute main.py by python main.py, it shows errors:

  File "main.py", line 1, in <module>
    from chart.chart_crawler import MelonChartCrawler
  File "/Users/Chois/Desktop/Programming/Project/WebScrap/MusicDownloader/chart/chart_crawler.py", line 4, in <module>
    from .. import util
ValueError: attempted relative import beyond top-level package

但是当我通过py.test test_chart_crawler.py在测试目录中执行测试代码时,它会起作用

But when I execute my test code in test directory by py.test test_chart_crawler.py, it works

当我初次面对绝对的相对输入时,它看起来非常简单直观.但这现在让我发疯.需要你的帮助.谢谢

When I first faced with absolute, relative imports, it seems like very easy and intuitive. But it drives me crazy now. Need your helps. Thanks

推荐答案

第一个问题是MusicDownloader不是软件包.将__init__.pymain.py一起添加到MusicDownloader,您的相对导入..chart应该可以工作.相对导入仅在包内部起作用,因此您不能..到非包文件夹.

The first problem is MusicDownloader not being a package. Add __init__.py to MusicDownloader along with main.py and your relative import ..chart should work. Relative imports work only inside packages, so you can't .. to non-package folder.

编辑我的帖子,为您的答案编辑提供更准确的答案.

Editing my post to provide you with more accurate answer to your answer edit.

关于__name__的全部.相对导入使用所用模块的__name__from .(.)部分来形成要导入的完整程序包/模块名称.简单解释一下,进口商的__name__from部分串联在一起,圆点表示要忽略/删除的名称中有多少部分,即:

It's all about the __name__. Relative imports use __name__ of the module they are used in and the from .(.) part to form a full package/module name to import. Explaining in simple terms importer's __name__ is concatenated with from part, with dots showing how many components of name to ignore/remove, i.e.:

__name__='packageA.packageB.moduleA'会导致导入packageA.packageB.moduleB的组合值,因此大致为from packageA.packageB.moduleB import something(但不是绝对导入,因为如果直接键入则为绝对导入).

__name__='packageA.packageB.moduleA' of the file containing line: from .moduleB import something, leads to combined value for import packageA.packageB.moduleB, so roughly from packageA.packageB.moduleB import something(but not absolute import as it would be if typed like that directly).

__name__='packageA.packageB.moduleA',导致导入packageA.moduleC的组合值,因此大致为from packageA.moduleC import something(但不是绝对导入,因为如果直接键入则为绝对导入).

__name__='packageA.packageB.moduleA' of the file containing line: from ..moduleC import something, leads to combined value for import packageA.moduleC, so roughly from packageA.moduleC import something(but not absolute import as it would be if typed like that directly).

这里是moduleB(C)还是packageB(C)并不重要.重要的是,在这两种情况下,我们仍然具有packageA部分作为相对导入的锚点".如果没有packageA部分,则不会解决相对导入,并且会出现类似尝试了超出顶级包的相对导入"之类的错误.

Here if it's a moduleB(C) or a packageB(C) doesn't really matter. What's important is that we still have that packageA part which works as an 'anchor' for relative import in both cases. If there will be no packageA part, relative import won't be resolved, and we'll get an error like "Attempted relative import beyond toplevel package".

这里需要注意的另一点是,当模块运行时,它会得到一个特殊的__name____main__,这显然会阻止它解决任何相对的导入问题.

One more note here, when a module is run it gets a special __name__ value of __main__, which obviously prevents it from solving any relative imports.

现在,根据您的情况,尝试将print(__name__)作为第一行添加到每个文件,并在不同的情况下运行文件,并查看输出如何变化.

Now regarding your case try adding print(__name__) as the very first line to every file and run your files in different scenarios and see how the output changes.

也就是说,如果直接运行main.py,您将获得:

Namely if you run your main.py directly, you'll get:

__main__
chart.chart_crawler
Traceback (most recent call last):
  File "D:\MusicDownloader\main.py", line 2, in <module>
    from chart.chart_crawler import MelonChartCrawler
  File "D:\MusicDownloader\chart\chart_crawler.py", line 2, in <module>
    from .. import util
ValueError: Attempted relative import beyond toplevel package

这里发生的是... main.py不知道MusicDownloader是一个软件包(即使在先前添加__init__.py的编辑之后).在您的chart_crawler.py:__name__='chart.chart_crawler'中,并使用from ..运行相对导入时,如上所述,包的组合值将需要去除两部分(每个点一个),因此结果将变为'',因为只是两个部分,没有封闭的包装.这会导致异常.

What happened here is... main.py has no idea about MusicDownloader being a package (even after previous edit with adding __init__.py). In your chart_crawler.py: __name__='chart.chart_crawler' and when running relative import with from .. the combined value for package will need to remove two parts (one for every dot) as explained above, so the result will become '' as there're just two parts and no enclosing package. This leads to exception.

导入模块时,将运行模块中的代码,因此它与执行模块几乎相同,但是不会使__name__成为__main__,并且不会封闭"包含的包(如果有的话).

When you import a module the code inside it is run, so it's almost the same as executing it, but without the __name__ becoming __main__ and the enclosing package, if there's any, being 'noticed'.

因此,解决方案是将main.py作为MusicDownloader包的一部分导入.要完成上述操作,请使用以下代码在与MusicDownloader文件夹相同的层次结构级别(附近,而不是在main.py附近的内部)中创建一个名为launcher.py的模块:

So, the solution is to import main.py as part of the MusicDownloader package. To accomplish the described above, create a module, say named launcher.py on the same level of hierarchy as MusicDownloader folder (near it, not inside it near main.py) with the following code:

print(__name__)
from MusicDownloader import main

现在运行launcher.py并查看更改.输出:

Now run launcher.py and see the changes. The output:

__main__
MusicDownloader.main
MusicDownloader.chart.chart_crawler
MusicDownloader.util

__main__launcher.py内部的__name__.在chart_crawler.py:__name__='MusicDownloader.chart.chart_crawler'内部,并使用from ..运行相对导入时,如上所述,包的组合值将需要除去两部分(每个点一个),因此结果将变为'MusicDownloader',而导入变为from MusicDownloader import util.正如我们在成功导入util.py的下一行所看到的那样,它将打印其__name__='MusicDownloader.util'.

Here __main__ is the __name__ inside launcher.py. Inside chart_crawler.py: __name__='MusicDownloader.chart.chart_crawler' and when running relative import with from .. the combined value for package will need to remove two parts (one for every dot) as explained above, so the result will become 'MusicDownloader' with import becoming from MusicDownloader import util. And as we see on the next line when util.py is imported successfully it prints its __name__='MusicDownloader.util'.

这就是它的全部-关于__name__的全部".

So that's pretty much it - "it's all about that __name__".

P.S.未提及的一件事是为什么使用test软件包的部件起作用.它不是以通用方式启动的,您使用了一些附加的模块/程序来对其进行处理,并且可能以某种方式将其导入,因此它可以正常工作.要了解这一点,最好看看该程序如何工作.

P.S. One thing not mentioned is why the part with test package worked. It wasn't launched in common way, you used some additional module/program to lauch it and it probably imported it in some way, so it worked. To understand this it's best to see how that program works.

官方文档中有一条注释:

请注意,相对导入基于当前模块的名称.由于主模块的名称始终为"__main__",因此打算用作Python应用程序主模块的模块必须始终使用绝对导入.

Note that relative imports are based on the name of the current module. Since the name of the main module is always "__main__", modules intended for use as the main module of a Python application must always use absolute imports.

这篇关于Python 3模块和程序包相对导入不起作用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆