【问题标题】:short import paths in PythonPython中的短导入路径
【发布时间】:2020-04-20 02:03:37
【问题描述】:

我回到 python 并遇到了导入路径问题。有人可以解释逻辑,或者更好的方法吗? 如果我有一个名为Sentence 的类,其结构类似于

base.py
models/Sentence.py

然后在base.py里面

import models.Sentence

为什么我还要这样做

s = models.Sentence.Sentence('arg')

我似乎也无法在导入时修复它

import models.Sentence as Sentence # ok but no help
import models.Sentence.Sentence as Sentence  # illegal and weird

我意识到我可以在 __init__ 模块文件中做一些魔术,但这似乎是一个魔术技巧。

每次创建类实例时都必须引用整个文件路径似乎很奇怪,所以我想知道我做错了什么。 JS 的显式导出和导入看起来更加清晰。

【问题讨论】:

  • 你试过from models.Sentence import Sentence吗?

标签: python python-3.x class import


【解决方案1】:

当您调用 import models.Sentence 时,您是在告诉 Python 加载您的 Sentence.py 文件,并将其所有变量放入 models.Sentence 命名空间中。因此,您的Sentence 类也列在models.Sentence 下,因此它变为models.Sentence.Sentence

就使用__init__.py而言,您可以在该文件中放入类似这样的内容:

from .Sentence import * # or import Sentence if that's all you want

然后在你的base.py:

import models

my_sentence = models.Sentence("arg")

您的__init__.py 现在所做的是将Sentence.py 中的所有内容加载到它自己的命名空间中,而不是models.Sentence。这意味着您的 Sentence 课程不再是 models.Sentence.Sentence,现在只是 models.Sentence

注意:如果您希望能够将__init__.py 文件作为主文件运行,或运行models 文件夹中的任何脚本,则应将此代码放在__init__.py 中:

import __main__, os

if os.path.dirname(__main__.__file__) == os.path.dirname(__file__):
    # executed script in this folder
    from Sentence import *
else:
    # executed script from elsewhere
    from .Sentence import *

编辑:

要了解为什么需要使用models.Sentence.Sentence('arg'),让我们看一个更清晰的示例:

假设我有一个名为package 的模块。在那个模块中,我有子模块this_useful_part.py

这是文件夹结构:

main.py
package
    this_useful_part.py

this_useful_part.py:

class my_very_helpful_class:
    pass

main.py:

import package.this_useful_part

my_instance = package.this_useful_part.my_very_helpful_class()

(我意识到我可能没有选择最好的名字)

我需要package.this_useful_part.my_very_helpful_class,而你需要models.Sentence.Sentence的原因是因为类不是文件,而是存储在文件中,文件不是 文件夹,但存储在文件夹中。

解决方法:

  • 你可以把类定义放在__init__.py里面,这样当你导入models时,就可以引用为models.Sentence

  • 1234563 /p>

我希望这可以解决问题。

编辑 2:

经过更多研究,我发现可以使模块可调用。 (来自here

如果您将Sentence.py 文件中的代码更改为以下代码,则不需要__init__.py 文件:

import sys

class Sentence:
    def __init__(self, arg):
        self.arg = arg

sys.modules[__name__] = Sentence

你现在可以做

import models.Sentence

my_sentence = models.Sentence('arg')

对不起,我的错误。

【讨论】:

  • 谢谢,但import models 肯定会在使用一个模型时导入所有模型?好像不是重点。同样在__init__ 文件中使用覆盖导出感觉就像是一种黑客行为。如果没有这些东西,真的没有办法通过它的类名来引用一个类吗?还有.Sentence文件名,里面有路径;;(
  • 您希望能够做什么?基本上,变量必须组织到命名空间中,因此调用models.Sentence('arg') 的唯一方法是执行上述操作之一,在__init__.py 中创建对Sentence 类的引用。
  • 通常我为每个文件保留一个类,所以这只会为导入增加很多冗余。我想导入类,而不是完整的路径。所以你的例子:my_instance = package.useful_part.helpful_class() 实际上是inst = package.class.class(opts),即重复。但是从您的附录中,我们可以以某种方式修补sys.modules 以添加(重复的)短路参考。两错不成对...
  • sys.modules[__name__] 行所做的是将模块的引用重新分配给您的类。因此,当您尝试调用模块时(通常不允许这样做),您正在调用您的类...
【解决方案2】:

我认为python文档解释得很好,我不能做得更好,所以我只是引用并提供链接。


Python documentation on packages (这只是与此问题相关的部分,但我强烈建议您打开链接并从那里阅读。)

包的用户可以从包中导入单个模块, 例如:

import sound.effects.echo

这会加载子模块 sound.effects.echo。它必须被引用 用它的全名。

sound.effects.echo.echofilter(input, output, delay=0.7, atten=4)

导入子模块的另一种方法是:

from sound.effects import echo

这也会加载子模块 echo,并使其在没有它的情况下可用 包前缀,所以可以这样使用:

echo.echofilter(input, output, delay=0.7, atten=4)

另一个变体是导入所需的函数或变量 直接:

from sound.effects.echo import echofilter

同样,这会加载子模块 echo,但这会使其功能 echofilter() 直接可用:

echofilter(input, output, delay=0.7, atten=4)

Documentation about the import statement (再次,我鼓励您打开链接并从那里阅读。)

基本的import语句(无from子句)分两步执行:

  1. 找到一个模块,必要时加载并初始化它
  2. 在本地命名空间中为作用域定义一个或多个名称 导入语句发生。

[...]

如果请求的模块被成功检索,它将被制作 通过以下三种方式之一在本地命名空间中可用:

  • 如果模块名称后跟as,那么as后面的名称直接绑定到导入的模块。
  • 如果没有指定其他名称,并且导入的模块是顶级模块,则模块的名称绑定在本地命名空间中 作为对导入模块的引用
  • 如果导入的模块不是顶级模块,则包含该模块的顶级包的名称绑定在 本地命名空间作为对顶级包的引用。这 必须使用其完整的限定名访问导入的模块 而不是直接

from 表单使用稍微复杂的过程:

  1. 找到from子句中指定的模块,加载并 必要时初始化它;
  2. 对于import 子句中指定的每个标识符:

    1. 检查导入的模块是否具有该名称的属性
    2. 如果没有,请尝试导入具有该名称的子模块,然后再次检查导入的模块的该属性
    3. 如果未找到该属性,则引发ImportError
    4. 否则,对该值的引用存储在本地命名空间中,使用as 子句中的名称(如果存在), 否则使用属性名称

例子:

import foo                 # foo imported and bound locally
import foo.bar.baz         # foo.bar.baz imported, foo bound locally
import foo.bar.baz as fbb  # foo.bar.baz imported and bound as fbb
from foo.bar import baz    # foo.bar.baz imported and bound as baz
from foo import attr       # foo imported and foo.attr bound as attr

最后,有关更多详细信息,您可以阅读the import system

【讨论】:

  • 欢迎来到 SO!是的,我阅读了文档,但这就是导致我陷入当前混乱的原因。我认为这是我的错误,但似乎 python 方式只是......有点不舒服。我猜你只是了解它的方式并习惯它。
猜你喜欢
  • 1970-01-01
  • 2019-07-07
  • 2018-04-26
  • 2013-04-16
  • 1970-01-01
  • 2020-12-02
  • 1970-01-01
  • 2019-12-03
  • 1970-01-01
相关资源
最近更新 更多