首页 > 解决方案 > Python3:模块'x'没有属性'y'

问题描述

Python import 语句让我很困惑。有人可以帮我解决这个问题吗?

文件树看起来像这样

root
+- notebook.ipynb
+- lib/
  +- basestation_config.py
  +- config.py
+- config/
  +- valence_pod.json
  +- etc…

config.py我有:

import json
import os

default_config_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'config'))

def read_filepath(filepath):
    with open(filepath, "r") as read_file:
        return json.load(read_file)

def read(filename):
    filepath = os.path.join(default_config_path, filename) + '.json'
    return read_filepath(filepath)

basestation_config.py我有:

import config as config
# … a buncha class libraries, including BasestationConfig
def read_basestation_config(config_name = 'valence_pod'):
    return BasestationConfig(config.read(config_name))

notebook.ipynb我有一个测试单元:

import lib.basestation_config as bsc
bs_config = bsc.read_basestation_config()
display(bs_config)

当我运行它时,我得到:

<module 'config' (namespace)>
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-1-f2942fb5fb2d> in <module>
      1 import lib.basestation_config as bsc
----> 2 bs_config = bsc.read_basestation_config()
      3 display(bs_config)

/mnt/eng_store/pod/logs/embedded/utils/logutils/indot-py/lib/basestation_config.py in read_basestation_config(config_name)
    270 def read_basestation_config(config_name = 'valence_pod'):
    271     print(config)
--> 272     return BasestationConfig(config.read(config_name))

AttributeError: module 'config' has no attribute ‘read’

标签: python-3.xpython-3.7

解决方案


当你使用configimport config文件夹时,Python 使用的是配置文件夹(带有 JSON 文件的文件夹)而不是模块。您可以在导入后通过打印来检查:lib/config.pyconfig.__path__

import config as config
print(config.__path__)
# _NamespacePath(['/path/to/root/config']) 

_NamespacePath表示文件夹config被视为隐式 Python 包,它不包含__init__.py类似常规 Python 包,但名称与正在导入的“config”匹配。

在查找名为“foo”的模块或包时,对于父路径中的每个目录:

  • 如果<directory>/foo/__init__.py找到,则导入并返回常规包。
  • 如果没有,但是<directory>/foo.{py,pyc,so,pyd}找到,一个模块被导入并返回。扩展的确切列表因平台和是否指定 -O 标志而异。这里的列表具有代表性。
  • 如果不是,但<directory>/foo找到并且是一个目录,则记录它并继续扫描父路径中的下一个目录。
  • 否则,扫描将继续父路径中的下一个目录。

您的设置属于第 3 条,与目标<directory>/config匹配import config。然后你可能想知道<directory>这里有什么?这取决于存储在 中的Module Search Pathsys.path,它是 Python 将在其中查找导入目标的所有目录的列表。当您在root下运行测试脚本时,目录会添加到您的sys.path.

包含正在运行的脚本的目录位于搜索路径的开头,位于标准库路径之前。

import config通过在basestation_config.py之前添加它来检查它:

import sys
print(sys.path)
# ['/path/to/root', ... ]

import config as config

这就解释了原因。要解决此问题,您可以执行以下操作:

  1. 将config文件夹重命名为其他名称(例如jsonfiles),只是为了防止将来出现此类错误并将其与config.py模块区分开来
  2. 更改lib以遵循常规的 Python 包结构,方法是在lib下添加一个__init__.py文件以清楚地将其标记为包。

    lib
    ├── __init__.py
    ├── basestation_config.py
    └── config.py
    
  3. 最后,在basestation_config.py中明确说明您将在同一目录 中导入config.py 。
    from . import config as config    
    print(config.__file__) 
    # /path/to/lib/config.py
    

请注意,在执行第 3 步之前,如果您添加了print(config.__path__)前面的代码,请确保在应用更正后的代码后将其删除,因为它很可能在您的config.py中不可用(您可能会收到“ AttributeError: module 'lib.config' has没有属性__path__")。

它现在应该在这些更改之后工作。


推荐阅读