从同名脚本导入已安装的包会引发"AttributeError:module has no attribute ",ImportError或NameError。
从同名脚本导入已安装的包会引发"AttributeError:module has no attribute ",ImportError或NameError。
我有一个命名为 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 import requests File "/Users/me/dev/rough/requests.py", line 3, in requests.get('http://www.google.ca') AttributeError: module 'requests' has no attribute 'get'
在更近版本的 Python 中,错误消息会变成 AttributeError: 部分初始化模块 \'requests\' 没有 \'get\' 属性(最可能是由于循环导入)
。
使用从导入方式导入一个具体的名称会导致 ImportError
错误:
from requests import get res = get('http://www.google.ca') print(res)
Traceback (most recent call last): File "requests.py", line 1, in from requests import get File "/Users/me/dev/rough/requests.py", line 1, in from requests import get ImportError: cannot import name 'get'
在更近版本的 Python 中,错误消息会变成 ImportError: 无法从部分初始化的模块 \'requests\' 中导入名称 \'get\'(最可能是由于循环导入)(/ Users / me / dev / rough / requests.py)
。
在包中使用从导入方式导入一个模块会导致不同的 ImportError
错误:
from requests.auth import AuthBase
Traceback (most recent call last): File "requests.py", line 1, in from requests.auth import AuthBase File "/Users/me/dev/rough/requests.py", line 1, in 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 from requests import * File "/Users/me/dev/rough/requests.py", line 3, in res = get('http://www.google.ca') NameError: name 'get' is not defined
错误的原因是用户创建的脚本与库文件名冲突。但需要注意的是,这个问题可能是间接造成的。需要一些侦探工作才能找出是哪个文件引起了问题。\n\n例如:假设你有一个名为mydecimal.py
的脚本,其中包括import decimal
,目的是使用标准库的decimal
库进行带小数位数值的准确浮点计算。这样不会引起问题,因为不存在标准库的mydecimal
。但是,decimal
导入了numbers
(另一个标准库模块)用于内部使用,因此你的项目中的名为numbers.py
的脚本会引起问题。\n\n在一个特别棘手的案例中,项目中有一个名为token.py
的文件(或在Python交互模式下启动时的当前工作目录),会导致交互式帮助无法正常显示:\n\n
$ 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 "", line 1, in File "/usr/lib/python3.8/_sitebuiltins.py", line 102, in __call__ import pydoc File "/usr/lib/python3.8/pydoc.py", line 66, in import inspect File "/usr/lib/python3.8/inspect.py", line 40, in import linecache File "/usr/lib/python3.8/linecache.py", line 11, in import tokenize File "/usr/lib/python3.8/tokenize.py", line 35, in from token import EXACT_TOKEN_TYPES ImportError: cannot import name 'EXACT_TOKEN_TYPES' from 'token' (/current/working/directory/token.py)
\n\n回溯告诉我们所有需要知道的信息:调用help
会触发延迟导入标准库pydoc
,该文件间接尝试导入标准库token
,但发现我们的token.py
并不包含适当的名称。在旧版本的Python中,情况甚至更糟: tokenize
会从token
进行星号导入,然后它的顶层代码会尝试在那里定义的名称,导致NameError
和堆栈跟踪没有提到token.py
。\n\n如果在项目中跟踪并重命名或删除适当的.py
文件后仍然遇到此类问题,则还要检查Python在导入模块时用于缓存字节码编译的.pyc
文件。在 3.x 中,这些文件将存储在具有特殊名称__pycache__
的文件夹中;可以安全地删除这些文件夹和文件,也可以抑制它们(但通常不希望这样做)。
这是因为你的本地模块命名为requests.py
,遮盖了你尝试使用的已安装的requests
模块。当前目录将作为sys.path
前缀,因此本地名称优先于已安装的名称。
当出现这种情况时,一个额外的调试提示是仔细查看Traceback,并意识到你的脚本名称与你尝试导入的模块名称匹配:
注意你在脚本中使用的名称:
File "/Users/me/dev/rough/requests.py", line 1, in
你尝试导入的模块:requests
将你的模块重命名为其他名称以避免名称冲突。
Python可能会在你的requests.py
文件旁边生成一个requests.pyc
文件(在Python 3的__pycache__
目录中)。重命名之后还应该把它删除,因为解释器仍然会引用该文件,从而重新产生错误。但是__pycache__
中的pyc
文件如果已经被删除,则不会影响你的代码。
在本例中,将文件重命名为my_requests.py
,删除requests.pyc
,然后再次执行成功打印
。
注意:当你将文件命名为你要导入的模块时,这不仅会发生。如果你将文件命名为直接导入模块所导入的模块的名称,则也可能发生这种情况。例如,有一个名为copy.py
的文件,并试图从那里import pandas
,将会出现
ImportError: cannot import name 'copy' from 'copy'
这是因为pandas
导入了copy
。这里没有神奇的解决方案,因为你不能知道世界上所有模块的名称,但有一个经验法则是尽可能使模块名称唯一,并在出现此类错误时尝试更改名称。