模块没有属性。

107

我有一个目录,其中包含多个 .py 文件。每个文件都定义了一些类。该目录中还有一个空的 __init__.py 文件。

例如:

myproject
    __init__.py
    mymodule
        __init__.py
        api.py
        models.py
        views.py

我正在尝试导入mymodule并访问所有这些文件中定义的类:

from myproject import mymodule

print mymodule.api.MyClass 

它给我一个错误,说mymodule没有api属性。为什么?而且为什么我只能访问其中一个文件(models.py)而不能访问其他文件呢?

In [2]: dir(banners)
Out[2]:
['__builtins__',
 '__doc__',
 '__file__',
 '__name__',
 '__package__',
 '__path__',
 'models']
4个回答

114

问题在于子模块不会被自动导入。您必须显式地导入api模块:

import myproject.mymodule.api
print myproject.mymodule.api.MyClass

如果你坚持在导入myproject.mymodule时需要可用的api,你可以将以下内容放在myproject/mymodule/__init__.py中:

import myproject.mymodule.api

那么这样就可以按预期工作:

from myproject import mymodule

print mymodule.api.MyClass 

1
你好,我尝试了这个方法,但是还是出现了ModuleNotFoundError: No module named lib.myfunction错误。你有什么想法吗?有趣的是,如果我只导入lib,就不会出错。但是如果在之后调用lib.myfunction时,也会出现错误。 - yl_low
这不是有点繁琐吗?有没有一种方法可以默认打开导入子模块(使用完全限定名称,例如foo.bar.baz)的选项? - Amir Rosenfeld
我可以完整导入,但在代码的其他部分中,我想删除 myproject 并引用似乎不可能的 mymodule.api - Alper
@AmirRosenfeld 没有这样的方法,也不可能有这样的方法,因为 文件系统实际上不指定 子模块的位置或名称。Python允许对导入系统进行大量自定义。除此之外,这将是一种可怕的默认行为,因为在大多数情况下,它会减慢所有顶级导入的速度,以便导入实际上后来并没有使用的大量代码。 - Karl Knechtel
@Alper 在包内使用相对导入将节省您大量的打字时间。更复杂的重命名也是可能的,但它们需要对导入系统的适当理解,这可能超出了甚至是单独的Stack Overflow Q&A的范围,更不用说评论部分了。 - Karl Knechtel

43

还要检查一下,你的 Python 文件名是否与你尝试导入的模块名相同。


9
是的,这是一个奇怪的Python评分标准,文件名必须与模块名不同。请注意! - CharlesW
5
哦,哇!这个很难在Google上搜索到。 - PatrickT
文件名不需要与模块名称不同。事实上,你正在实现的模块的文件名,根据定义,决定了该模块的名称。这是一个完全无关的问题,当你想要从当前文件中导入一个不同的模块,但当前文件与你想要导入的另一个模块具有相同的名称时,会出现这个问题。问题在于每个 Python 源代码文件都定义了一个模块,无论你是否打算将其import进来。因此,源代码文件定义的模块可以尝试自我导入。 - Karl Knechtel

2

模块不是那样工作的。

from myproject.mymodule import api
print api.MyClass

我知道我可以做到。使用点名称是否可能实现? - akonsu
2
@akonsu,只需执行import myproject.mymodule.api,然后通过myproject.mymodule.api.MyClass访问即可。 - Rob Wouters
我认为这是关于语言本身的问题,而不是关于如何让我的代码工作的问题。我很好奇是否可以实现这一点。我能否通过“mymodule.api…”访问我的类,而不需要前缀“myproject”? - akonsu
你可以使用import myproject.mymodule as mymodule,但我觉得这样会让人感到困惑。 - Matt

-5
你在myproject目录中也需要一个__init__.py文件。所以你的模块结构应该是这样的:
myproject
    __init__.py
    mymodule
        __init__.py
        api.py
        models.py
        views.py

11
那正是问题中的模块结构... - SenhorLucas
除此以外,自Python 3.3版本开始,空的__init__.py文件不再必须用于设置目录作为包(尽管它们确实会影响绝对导入的查找顺序)。在现代Python中,缺少__init__.py文件不是相对导入失败的原因。 - Karl Knechtel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接