从同名脚本(或附近脚本)导入库会引发“AttributeError:模块没有属性”或 ImportError 或 NameError

2024-11-15 08:36:00
admin
原创
21
摘要:问题描述:我有一个名为的脚本requests.py需要使用第三方requests包。该脚本无法导入该包,或者无法访问其功能。为什么它不起作用?我该如何修复它?尝试简单导入然后使用该功能会导致AttributeError:import requests res = requests.get('http://w...

问题描述:

我有一个名为的脚本requests.py需要使用第三方requests包。该脚本无法导入该包,或者无法访问其功能。

为什么它不起作用?我该如何修复它?

尝试简单导入然后使用该功能会导致AttributeError

import requests

res = requests.get('http://www.google.ca')
print(res)
Traceback (most recent call last):
  File "/Users/me/dev/rough/requests.py", line 1, in <module>
    import requests
  File "/Users/me/dev/rough/requests.py", line 3, in <module>
    requests.get('http://www.google.ca')
AttributeError: module 'requests' has no attribute 'get'

在较新版本的 Python 中,错误消息改为:AttributeError: partially initialized module 'requests' has no attribute 'get' (most likely due to a circular import)

使用特定名称的 from-import 会导致ImportError

from requests import get

res = get('http://www.google.ca')
print(res)
Traceback (most recent call last):
  File "requests.py", line 1, in <module>
    from requests import get
  File "/Users/me/dev/rough/requests.py", line 1, in <module>
    from requests import get
ImportError: cannot import name 'get'

在较新版本的 Python 中,错误消息改为:ImportError: cannot import name 'get' from partially initialized module 'requests' (most likely due to a circular import) (/Users/me/dev/rough/requests.py)

对包内的模块使用 from-import 会导致不同的结果ImportError

from requests.auth import AuthBase
Traceback (most recent call last):
  File "requests.py", line 1, in <module>
    from requests.auth import AuthBase
  File "/Users/me/dev/rough/requests.py", line 1, in <module>
    from requests.auth import AuthBase
ImportError: No module named 'requests.auth'; 'requests' is not a package

使用星号导入然后使用该功能会引发NameError

from requests import *

res = get('http://www.google.ca')
print(res)
Traceback (most recent call last):
  File "requests.py", line 1, in <module>
    from requests import *
  File "/Users/me/dev/rough/requests.py", line 3, in <module>
    res = get('http://www.google.ca')
NameError: name 'get' is not defined

如果您故意将模块命名为与现有模块相同的名字并想要处理这种情况,请参阅当我的项目有一个同名的模块时,如何从标准库导入?(如何控制 Python 在哪里查找模块?)


解决方案 1:

发生这种情况的原因是,名为的本地模块requests.py会遮盖requests您尝试使用的已安装模块。当前目录被添加到 前面sys.path,因此本地名称优先于已安装的名称。

出现这种情况时,一个额外的调试技巧是仔细查看 Traceback,并意识到所讨论的脚本的名称与您尝试导入的模块相匹配:

注意您在脚本中使用的名称:

File "/Users/me/dev/rough/requests.py", line 1, in <module>

您尝试导入的模块:requests

将您的模块重命名为其他名称以避免名称冲突。

Python 可能会在您的文件requests.pyc旁边生成一个文件(在 Python 3 中的目录中)。重命名后也请删除该文件,因为解释器仍将引用该文件,从而重新生成错误。但是,如果该文件已被删除,则该文件不会影响您的代码。requests.py`__pycache__pycpycache py`

在示例中,将文件重命名为my_requests.py,删除requests.pyc,然后再次运行即可成功打印<Response [200]>


注意:这种情况不仅发生在将文件命名为您尝试导入的模块时。如果您将文件命名为与您直接导入的模块导入的模块相同的名称,也会发生这种情况。例如,调用一个文件copy.py并尝试import pandas从那里执行,将导致

ImportError: cannot import name 'copy' from 'copy'

这是因为pandasimports copy。这里没有神奇的解决方案,因为您不可能知道世界上所有模块的名称,但是经验法则是尝试使模块名称尽可能唯一,并在出现此类错误时尝试更改名称。

解决方案 2:

发生此错误的原因是用户创建的脚本与库文件名发生名称冲突。但请注意,此问题可能是间接导致的。可能需要进行一些侦查工作才能找出导致问题的文件。

例如:假设您有一个mydecimal.py包含 的脚本import decimal,打算使用标准库decimal库进行带有十进制数的精确浮点计算。这不会造成问题,因为没有标准库mydecimal。但是,恰巧 导入了decimalnumbers另一个标准库模块)供内部使用,因此在您的项目中调用的脚本numbers.py会导致问题。

.py如果在跟踪自身并重命名或删除项目中的相应文件后仍然遇到此类问题,还请检查Python 在导入模块时用于缓存字节码编译的.pyc文件。在 3.x 中,这些文件将存储在具有特殊名称的文件夹中;删除此类文件夹和文件是安全的,并且可以隐藏它们(但您通常不想这样做)。__pycache__

解决方案 3:

概括

当项目中的 Python 源文件与某些外部库模块(标准库或已安装的第三方包)同名时,就会出现此类问题。当尝试import从外部库(需要使用绝对导入)时,会发现项目自己的模块,从而导致各种错误,具体取决于具体细节。

一般情况下,修复该问题最简单的方法是重命名受影响的文件。可能还需要找到并删除任何相应的文件.pyc。(这是非常安全的;这些文件只是之前将 Python 源代码转换为 Python 虚拟机的字节码的工作的缓存.class,类似于Java 中的文件。)

当首选项目自己的模块时,这是因为 Python 搜索模块源代码以进行绝对导入的方式。根据 Python 的具体启动方式,中定义的模块搜索路径sys.path通常从当前项目内的路径开始。在查看任何标准库或第三方库文件夹之前,Python 将首先在那里查看。这个搜索过程不关心导入模块所在的文件夹;任何相对路径都是相对于进程的当前工作目录,而不是导入模块。(但是,标准库路径通常是绝对路径,并且靠近的末尾sys.path。)

AttributeError

出现此AttributeError 错误是因为项目自己的模块没有定义调用代码想要从外部库模块使用的函数、类等。因此,'module' object代表具有指定名称的项目模块的has no attribute,所以这正是错误消息所声称的。

在极少数不幸的情况下,项目自己的模块可能会定义具有相同名称的内容,从而执行不同的操作。这可能会导致任何类型的异常或其他逻辑错误。例如,如果修改了问题中的第一个示例:

import requests

def get():
    pass

res = requests.get('http://www.google.ca')
print(res)

现在TypeError将会引发,因为代码将尝试get使用错误数量的参数来调用本地定义的函数。

ImportError

使用特定的 from-import 会导致错误报告不同,因为现在在导入过程本身中检测到了问题。在问题示例中,from requests import get意味着,不是创建一个requests命名模块的新全局名称,而是应该有一个新的全局名称get来命名该模块中的函数(在普通导入后将被引用的函数requests.get)。这需要从该模块查找get属性;但由于加载了错误的模块,该属性查找失败。较新版本的 Python 将此报告为可能的循环导入。

其他导入尝试可能会导致不同的ImportErrors 抱怨导入的模块“不是包”。这是不言自明的:在问题的示例中,requests是一个普通模块(因为它是由源代码文件定义的requests.py),但所需的requests- 由第三方库定义的 - 是一个包(由requests其他地方的文件夹中的几个文件定义,包括一个__init__.py定义一些不是模块的顶级包内容的文件,例如get函数)。

NameError

使用星型导入(如from requests import *)不会直接失败 - 它会为所有模块内容创建全局名称。但是,由于问题示例中的模块不包含任何名为 的内容get,因此其尝试星型导入本身也不会定义该名称。因此,NameError会发生 —— 尝试使用不存在的全局名称的正常错误。

加重因素

  1. Python 的默认import系统基于名称,而不是文件路径,并且 Python不区分“(驱动程序)脚本文件”和“库(模块)文件”。任何 Python 源代码都可以被import编译。尽管 Python 缓存了模块导入,但主脚本通常不会在此缓存中,这意味着它完全有能力尝试import自身。这就是问题示例中发生的情况:由于requests尚未被import编译,缓存中没有任何内容,因此在驱动程序脚本(名为requests.py)中尝试import requests将搜索要导入的内容,并找到驱动程序脚本(即其自己的源文件)。

只会在导入时导致问题,如果模块尝试在其自己的初始化中使用该导入,例如通过执行 from-import。否则,问题将被推迟,导致(通常)AttributeErrorNameError在尝试使用该功能时。另请参阅:

* 使用相互或循环导入时会发生什么?
* 为什么循环导入似乎在调用堆栈的上层起作用,但随后在下面引发 ImportError ?
  1. 每当加载模块时(即从其源导入,而不是使用缓存),它自己的顶级import语句就会运行,从而递归地导致更多导入。任何这些间接导入都可能找到本地代码。Python 标准库不是包,并且主要使用绝对导入来引用其其他组件。例如,正如 Dave Rove 的回答所指出的那样,尝试decimal从名为 的源文件numbers.py或在具有此类源文件的项目中导入标准库模块可能会失败。

有一种特别有害的情况token.py,项目中(或以交互模式启动 Python 时,当前工作目录)有一个名为的文件会导致交互式帮助中断

$ touch token.py
$ python
Python 3.8.10 (default, Nov 14 2022, 12:59:47) 
[GCC 9.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> help
Type help() for interactive help, or help(object) for help about object.
>>> help()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3.8/_sitebuiltins.py", line 102, in __call__
    import pydoc
  File "/usr/lib/python3.8/pydoc.py", line 66, in <module>
    import inspect
  File "/usr/lib/python3.8/inspect.py", line 40, in <module>
    import linecache
  File "/usr/lib/python3.8/linecache.py", line 11, in <module>
    import tokenize
  File "/usr/lib/python3.8/tokenize.py", line 35, in <module>
    from token import EXACT_TOKEN_TYPES
ImportError: cannot import name 'EXACT_TOKEN_TYPES' from 'token' (/current/working directory/token.py)

回溯告诉我们所有需要知道的信息:调用help会触发标准库的延迟导入pydoc,这会间接尝试导入标准库token,但会发现我们的token.py不包含适当的名称。在旧版本的 Python 中,情况甚至更糟:tokenize会从进行星型导入token,然后其顶级代码会尝试使用在那里定义的名称,导致NameError- 并且堆栈跟踪不会提及文件名token.py

  1. .pyc即使相应的源文件被重命名或删除, Python 也可以从缓存的字节码文件 ( ) 导入。这是为了加快导入速度,但它增加了一个额外的步骤来保持代码目录“干净”。在重命名任何.py文件以解决此类问题后,请确保也检查字节码缓存。

解决问题

当然,标准建议简单明了:只需重命名.py错误导入的文件,并删除.pyc这些模块的所有缓存字节码文件。默认情况下,在 Python 3.x 中,文件夹中.pyc任何.py文件的文件都将放入具有特殊名称的子文件夹中__pycache__;在 2.x 中,它们只是出现在相应.py文件旁边。

然而,虽然这种方法可以快速解决大多数遇到此问题的人的直接问题,但该建议的可扩展性并不好。有很多潜在的问题名称;虽然大多数项目对包含名为 eg 的源文件不感兴趣os.py,但其他一些名称可能更可取,或者更难解决。因此,以下是其他一些有用的技巧:

控制sys.path

当然,由于问题是由于sys.path指定绝对导入应首先查看当前项目而引起的,因此只需更改 即可避免该问题sys.path

文档中将有问题的路径描述为“潜在的不安全路径”。使用交互式 Python 提示符时,它将是一个空字符串(相当于 的相对路径'.') - 即 Python 进程的当前工作目录,反映了使用 等所做的任何更改os.chdir。对于正常启动的驱动程序脚本(python driver.py),它将是脚本所在的目录,作为绝对路径(不一定是当前工作目录,因为可以在命令行上指定路径,如python path/to/driver.py)。对于使用命令行标志运行的模块-m,它将是初始当前工作目录(不一定是模块所在的位置,但也是不会受到影响的绝对os.chdir路径)。

为了避免出现此路径sys.path,请执行以下操作之一

  • 在 Python 3.11 及更高版本中,设置PYTHONSAFEPATH环境变量,或使用Python 的-P命令行选项。

  • 在 Python 3.4 及更高版本中,使用-I命令行选项以隔离模式启动。但是,这还有其他几个影响:它将忽略环境变量(如PYTHONPATHPYTHONHOME),并且它还将跳过将用户特定的 site-packages 目录添加到路径(因此代码将无法访问使用 Pip 选项安装的第三方库--user)。

  • 如果所有其他方法都失败了,请考虑手动操作sys.path。这很混乱且容易出错,但这sys.path只是带有文件路径的普通字符串列表,修改其内容将影响将来的import。(不要尝试替换列表对象;这不会起作用,因为 Python 不会通过名称查找它sys.path,而是使用硬编码的内部引用。无法从 Python 中销毁或替换该对象;sys.path只是一个初始化以引用它的名称。)

请记住,删除“不安全”路径将阻止有意的绝对导入在包内工作。这对于一些小项目来说很不方便,但也是学习正确的包组织的一个很好的理由。说到这个:

在包中使用相对导入

一个组织良好的 Python 项目通常由一个或多个(通常只有一个)包组成,存储在主项目文件夹的子文件夹中,以及一个或多个放置在包子文件夹之外的驱动程序脚本。驱动程序脚本通常使用单个绝对导入来访问包功能,同时实现一些简单的包装器逻辑(例如解析命令行参数、格式化和报告未捕获的异常等)。同时,包将始终使用相对导入来获取其自身内容,并仅在需要访问其他包(标准库和第三方依赖项)时使用绝对导入。

例如,一个项目可能按如下方式组织:

project
├── src
│   └── my_package
│       └── x.py
│       └── y.py
│       └── z.py
└── driver.py

代码driver.py将使用绝对导入到包中,例如:

from my_package.x import entry_point

if __name__ == '__main__':
    entry_point()

(如果需要逻辑来解析命令行参数,它通常应该放在驱动程序中而不是包的代码中。)

然后,包内的代码将使用相对导入- 因此x.py可能包含类似以下内容:

from .y import first_thing
from .z import second_thing

def entry_point():
    first_thing()
    second_thing()

这样可以兼顾两方面:初始绝对导入设置顶级包,以便相对导入可以工作,而相对导入将避免依赖于配置sys.path。即使不采取配置步骤sys.path,它通常也会包含包含驱动程序脚本的文件夹,但不包含任何包文件夹;因此,这也会自动避免导入路径冲突。(除非指定相应的包路径,否则绝对导入将找不到包内容;但通常当它找到时,从当前包导入是故意的。)

这也避免了为下一个依赖项目的项目设置陷阱。例如,假设我们实现并发布了一个API包,而其他人编写了依赖此项目的Client包。由于代码将使用相对导入来实现其他功能(例如),因此项目本身不会造成问题。API`APIAPIfrom . import functionalityClientfunctionality.py`

当然,为了使初始绝对导入正常工作,需要在 中提及顶级包文件夹。但是,这通常通过安装包sys.path来完成,因此不会造成问题。

禁用字节码缓存

如果需要重命名文件,.pyc如果文件根本不存在,那么避免文件出现问题会更容易。毕竟,它们不是必需的;它们只是为了加快程序后续运行的导入速度。

要抑制.pyc文件生成,请使用-B命令行选项或设置PYTHONDONTWRITEBYTECODE环境变量。(没有内置解决方案可以删除所有现有.pyc文件,但使用标准库模块等手动实现这一点很容易shutil。)

避免使用有问题的名称

考虑使用第三方工具(例如 IDE 插件)来警告项目中标准库或第三方库使用的文件名。在 Python 3.10 及更高版本中,标准库模块名称的完整列表也可用作sys.stdlib_module_names。这包括当前 Python 安装中可能不存在的名称(例如特定于操作系统的组件或有时被禁用或省略的内容,例如 Tkinter)。

解决方案 4:

只需将导致错误的导入放到最后即可

ex:
import requests
import flask
import numpy

如果import flask导致错误则将其移至导入的底部

solution:-
import requests
import numpy
import flask

解决方案 5:

循环依赖:当两个或多个模块相互依赖时就会发生这种情况。这是因为每个模块都是根据另一个模块定义的。

如果您收到类似于以下请求模块错误的循环导入错误。

AttributeError: partially initialized module 'requests' has no attribute 'post' (most likely due to a circular import)

Please try to rename the file.这个错误的错误通常是由于与您尝试导入requests模块的文件名冲突造成的。

我也遇到了同样的问题,我的文件名是email.py,我试图导入请求模块。因此,它与发生了一些冲突email.parser。因此,我将文件名从 更改为email.pyemail1.py然后它就成功了。

有关循环依赖的更多信息:https://stackabuse.com/python-circular-imports/

相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   601  
  华为IPD与传统研发模式的8大差异在快速变化的商业环境中,产品研发模式的选择直接决定了企业的市场响应速度和竞争力。华为作为全球领先的通信技术解决方案供应商,其成功在很大程度上得益于对产品研发模式的持续创新。华为引入并深度定制的集成产品开发(IPD)体系,相较于传统的研发模式,展现出了显著的差异和优势。本文将详细探讨华为...
IPD流程是谁发明的   7  
  如何通过IPD流程缩短产品上市时间?在快速变化的市场环境中,产品上市时间成为企业竞争力的关键因素之一。集成产品开发(IPD, Integrated Product Development)作为一种先进的产品研发管理方法,通过其结构化的流程设计和跨部门协作机制,显著缩短了产品上市时间,提高了市场响应速度。本文将深入探讨如...
华为IPD流程   9  
  在项目管理领域,IPD(Integrated Product Development,集成产品开发)流程图是连接创意、设计与市场成功的桥梁。它不仅是一个视觉工具,更是一种战略思维方式的体现,帮助团队高效协同,确保产品按时、按质、按量推向市场。尽管IPD流程图可能初看之下显得错综复杂,但只需掌握几个关键点,你便能轻松驾驭...
IPD开发流程管理   8  
  在项目管理领域,集成产品开发(IPD)流程被视为提升产品上市速度、增强团队协作与创新能力的重要工具。然而,尽管IPD流程拥有诸多优势,其实施过程中仍可能遭遇多种挑战,导致项目失败。本文旨在深入探讨八个常见的IPD流程失败原因,并提出相应的解决方法,以帮助项目管理者规避风险,确保项目成功。缺乏明确的项目目标与战略对齐IP...
IPD流程图   8  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用