从(或附近)导入具有相同名称的脚本提高“属性:模块没有属性”的脚本。或Importerror或Nameerror

发布于 2025-01-19 19:20:01 字数 3365 浏览 3 评论 0 原文

我有一个名为 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:部分初始化的模块“requests”没有属性“get”(很可能是由于循环导入)

使用特定名称的 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:无法从部分初始化的模块“requests”导入名称“get” (很可能是由于循环导入)(/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 3.13 开始,错误从 文档 中,消息变得非常清晰:

一个常见的错误是编写与标准同名的脚本 库模块。当这导致错误时,我们现在显示更多 有用的错误消息:

$ python random.py Traceback(最近一次调用最后一次):文件
“/home/me/random.py”,第 1 行,在  中
    导入随机文件“/home/me/random.py”,第 3 行,在  中
    打印(随机.randint(5))
          ^^^^^^^^^^^^^^^ AttributeError:模块“random”没有属性“randint”(考虑重命名“/home/me/random.py”,因为它
与名为“random”的标准库模块同名并且
防止导入该标准库模块)

类似地,如果 脚本与它尝试的第三方模块具有相同的名称 导入并导致错误,我们还显示了更有用的 错误消息:

$ python numpy.py Traceback(最近一次调用最后一次):文件
“/home/me/numpy.py”,第 1 行,在  中
    import numpy as np File "/home/me/numpy.py", line 3, in ;
    np.array([1,2,3])
    ^^^^^^^^ AttributeError:模块“numpy”没有属性“array”(如果“/home/me/numpy.py”与
您打算导入的库)

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

I have a script named requests.py that needs to use the third-party requests package. The script either can't import the package, or can't access its functionality.

Why isn't this working, and how do I fix it?

Trying a plain import and then using the functionality results in an 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'

In more recent versions of Python, the error message instead reads AttributeError: partially initialized module 'requests' has no attribute 'get' (most likely due to a circular import).

Using from-import of a specific name results in an 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'

In more recent versions of Python, the error message instead reads ImportError: cannot import name 'get' from partially initialized module 'requests' (most likely due to a circular import) (/Users/me/dev/rough/requests.py).

Using from-import for a module inside the package results in a different 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

Using a star-import and then using the functionality raises a 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

From Python 3.13 onwards the error message becomes very clear, from the documentation:

A common mistake is to write a script with the same name as a standard
library module. When this results in errors, we now display a more
helpful error message:

$ python random.py Traceback (most recent call last):   File
"/home/me/random.py", line 1, in <module>
    import random   File "/home/me/random.py", line 3, in <module>
    print(random.randint(5))
          ^^^^^^^^^^^^^^ AttributeError: module 'random' has no attribute 'randint' (consider renaming '/home/me/random.py' since it
has the same name as the standard library module named 'random' and
prevents importing that standard library module)

Similarly, if a
script has the same name as a third-party module that it attempts to
import and this results in errors, we also display a more helpful
error message:

$ python numpy.py Traceback (most recent call last):   File
"/home/me/numpy.py", line 1, in <module>
    import numpy as np   File "/home/me/numpy.py", line 3, in <module>
    np.array([1, 2, 3])
    ^^^^^^^^ AttributeError: module 'numpy' has no attribute 'array' (consider renaming '/home/me/numpy.py' if it has the same name as a
library you intended to import)

For cases where you name your module the same as an existing one on purpose and want to handle that situation, see How can I import from the standard library, when my project has a module with the same name? (How can I control where Python looks for modules?)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

ぶ宁プ宁ぶ 2025-01-26 19:20:01

之所以发生这种情况,是因为您的本地模块名为 requests.py 阴影已安装的请求您要尝试使用的模块。当前目录被添加到 sys.path ,因此本地名称优先于已安装的名称。

当出现此内容时,一个额外的调试提示是仔细查看回溯,并意识到所讨论的脚本的名称与您要导入的模块匹配:

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

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

您正在尝试的模块导入:请求

将您的模块重命名为其他东西,以避免名称碰撞。

Python可能会在您的 requests.pyss.pyss.pyc requests.pys.py file(在 __ pycache __ pycache __ in python 3中)。重命名后也将其删除,因为解释器仍将引用该文件,从而重新产生错误。但是, pyc __ pycache __ 应该如果 py> py 文件已删除,则不会影响您的代码。

在示例中,将文件重命名为 my_requests.py ,删除 requests.pyc ,然后再次成功打印&lt; wenspys [200]&gt;


注意: 这不仅发生在将文件命名为要导入的模块时。如果您将文件命名与直接导入的模块导入的模块相同,则可能会发生这种情况。例如,拥有一个名为 copy.py 的文件,然后尝试从那里导入pandas

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

因为 pandas 导入复制。这里没有魔术解决方案,因为您不知道世界上所有模块的名称,但是经验法则是尝试使模块的名称尽可能独特,并在遇到此类错误时尝试更改名称。

This happens because your local module named requests.py shadows the installed requests module you are trying to use. The current directory is prepended to sys.path, so the local name takes precedence over the installed name.

An extra debugging tip when this comes up is to look at the Traceback carefully, and realize that the name of your script in question is matching the module you are trying to import:

Notice the name you used in your script:

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

The module you are trying to import: requests

Rename your module to something else to avoid the name collision.

Python may generate a requests.pyc file next to your requests.py file (in the __pycache__ directory in Python 3). Remove that as well after your rename, as the interpreter will still reference that file, re-producing the error. However, the pyc file in __pycache__ should not affect your code if the py file has been removed.

In the example, renaming the file to my_requests.py, removing requests.pyc, and running again successfully prints <Response [200]>.


Note: This doesn't only happen when naming your file as the module you are trying to import. This can also happen if you name your file the same as a module imported by a module you import directly. For example, having a file called copy.py and trying to import pandas from there, will give

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

That is because pandas imports copy. There is no magic solution here as you can't know all the modules' names in the world, but a rule of thumb is to try to make names of modules as unique as possible and try to change the name whenever you get such error.

究竟谁懂我的在乎 2025-01-26 19:20:01

发生错误是因为用户创建的脚本具有带有库文件名的名称扣。但是请注意,问题可能是间接引起的。可能需要一些侦探工作才能确定哪个文件引起了问题。

例如:假设您有一个脚本 myDecimal.py ,其中包括导入小数,打算使用标准库十进制库,以进行准确的浮动 - 点数的点数十进制数字。这不会引起问题,因为没有标准库 myDecimal 。但是,碰巧的是,十进制 imports 数字(另一个标准库模块)用于内部使用会导致问题。

如果在项目中跟踪和重命名或删除适当的 .py 文件后仍然遇到这样的问题,请检查 .py> .pyc files 导入模块时使用Python用于缓存字节码编译。在3.x中,这些将存储在带有特殊名称 __ pycache __ 的文件夹中。 delete 这样的文件夹和文件,以及可能抑制它们(但通常您不想)。

The error occurs because a user-created script has a name-clash with a library filename. Note, however, that the problem can be caused indirectly. It might take a little detective work to figure out which file is causing the problem.

For example: suppose that you have a script mydecimal.py that includes import decimal, intending to use the standard library decimal library for accurate floating-point calculations with decimal numbers. That doesn't cause a problem, because there is no standard library mydecimal. However, it so happens that decimal imports numbers (another standard library module) for internal use, so a script called numbers.py in your project would cause the problem.

If you still encounter problems like this after tracking own and renaming or removing the appropriate .py files in your project, also check for .pyc files that Python uses to cache bytecode compilation when importing modules. In 3.x, these will be stored in folders with the special name __pycache__; it is safe to delete such folders and files, and possible to suppress them (but you normally won't want to).

对你的占有欲 2025-01-26 19:20:01

摘要

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

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

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

AttributeError

An AttributeError 发生是因为项目自己的模块根本没有定义调用代码想要从外部库模块使用的函数、类等。因此,表示项目模块'module'对象没有具有指定名称的属性,因此这正是错误消息所声明的内容。

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

import requests

def get():
    pass

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

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

ImportError

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

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

NameError

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

加重因素

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

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

  2. 每当加载模块时(即从其源导入,而不是使用缓存) ),有自己的顶层import 语句运行,导致更多的递归导入。任何这些间接导入都可能找到本地代码。 Python 标准库不是一个包,并且大多使用绝对导入来引用其其他组件。例如,正如戴夫·罗夫(Dave Rove)的回答中所指出的,当从名为 numbers.py 的源文件或在项目中尝试导入标准库 decimal 模块时,可能会失败有这样一个源文件。

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

    $ touch token.py
    $ 蟒蛇
    Python 3.8.10(默认,2022 年 11 月 14 日,12:59:47) 
    Linux 上的 [GCC 9.4.0]
    输入“帮助”、“版权”、“制作人员”或“许可证”以获取更多信息。
    >>>>>帮助
    键入 help() 以获得交互式帮助,或键入 help(object) 以获得有关对象的帮助。
    >>>>>帮助()
    回溯(最近一次调用最后一次):
      文件“”,第 1 行,在  中
      文件“/usr/lib/python3.8/_sitebuiltins.py”,第 102 行,在 __call__ 中
        导入pydoc
      文件“/usr/lib/python3.8/pydoc.py”,第 66 行,在  中
        进口检验
      文件“/usr/lib/python3.8/inspect.py”,第 40 行,在  中
        导入行缓存
      文件“/usr/lib/python3.8/linecache.py”,第 11 行,在  中
        导入标记化
      文件“/usr/lib/python3.8/tokenize.py”,第 35 行,在  中
        从令牌导入 EXACT_TOKEN_TYPES
    ImportError:无法从“令牌”(/current/working Directory/token.py)导入名称“EXACT_TOKEN_TYPES”
    

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

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

解决问题

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

然而,虽然这种方法可以快速解决大多数遇到这种问题的人的紧迫问题,但该建议的扩展性却不是很好。有很多可能有问题的名字;虽然大多数项目对包含名为 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 环境变量,或使用 -P 命令行选项 Python。

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

  • 如果所有其他方法都失败,请考虑手动操作sys.path。这很混乱并且容易出错,但是 sys.path 只是一个带有文件路径的普通字符串列表,修改其内容将影响将来的导入。 (不要尝试替换列表对象;这不会起作用,因为 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()

这得到了两全其美:最初的绝对值import 设置顶级包,以便相对导入起作用,并且相对导入将避免依赖于 sys.path 配置。即使不采取步骤配置 sys.path,它通常也会包含包含驱动程序脚本的文件夹,但不包含任何包文件夹;因此,这也自动避免了导入路径冲突。 (绝对导入不会找到包内容,除非它指定了相应的包路径;但通常当它这样做时,从当前包导入是有意的。)

这也避免了为下一个项目设置陷阱其中这个作为依赖项。举例来说,我们实现并发布了一个 API 包,而其他人编写了 Client,其中包含 API 作为依赖项。由于 API 代码将为其他 API 功能(例如,from .import 功能)使用相对导入,因此 Client项目自己的功能性.py 不会引起问题。

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

禁用字节码缓存

如果需要重命名文件,那么如果 .pyc 文件一开始就不存在,那么避免这些文件出现问题会更容易。毕竟,它们没有必要;同样,它们只是为了加快程序后续运行时的导入速度。

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

)避免有问题的名称

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

Summary

Problems such as this occur when a Python source file in the project has the same name as some external library module (either in the standard library or an installed third-party package). When attempting to import from the external library (which requires using an absolute import), the project's own module is found instead, causing various errors depending on the exact details.

The easiest way to fix the problem, in ordinary cases, is to rename the affected file. It may also be necessary to locate and delete any corresponding .pyc files. (This is perfectly safe; the files are just a cache of the work that was previously done to translate Python source code into bytecode for the Python virtual machine, analogous to .class files in Java.)

When the project's own module is preferred, this is because of how Python searches for module source code for absolute imports. Depending on exactly how Python is started, the module search path defined in sys.path will usually start with a path that is within the current project. Python will look there first, before looking in any standard library or third-party library folders. This search process does not care about the folder where the importing module is located; any relative paths are relative to the process' current working directory, not the importing module. (However, the standard library paths will normally be absolute paths, and near the end of sys.path, anyway.)

AttributeError

An AttributeError occurs because the project's own module simply doesn't define the function, class etc. that the calling code wants to use from the external library module. As a result, the 'module' object that represents the project's module has no attribute with the specified name, so that's exactly what the error message claims.

In rare, unfortunate cases, the project's own module might happen to define something with the same name such that it does something different. This can cause any kind of exception or other logical error. For example, if the first example from the question is modified:

import requests

def get():
    pass

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

now a TypeError will be raised instead, because the code will attempt to call the locally-defined get function with the wrong number of arguments.

ImportError

Using a specific from-import causes the error to be reported differently because the problem is now detected during the importing process itself. In the example in the question, from requests import get means, instead of creating a new global name requests which names the module, there should be a new global name get which names the function from that module (the one which would be referred to as requests.get after a plain import). This requires looking up the get attribute from that module; but since the wrong module was loaded, that attribute lookup fails. More recent versions of Python report this as a probable circular import.

Other import attempts can cause different ImportErrors which complain that the imported module "is not a package". This is self-explanatory: in the example in the question, requests is an ordinary module (because it is defined by a source code file, requests.py), but the desired requests - defined by the third-party library - is a package (defined by several files inside a requests folder elsewhere, including an __init__.py which defines some top-level package contents that are not modules, like the get function).

NameError

Using a star-import, like from requests import *, will not fail directly - it creates global names for all the module contents. However, since the module in the question example doesn't contain anything named get, its attempt to star-import itself will not define that name either. Thus, a NameError occurs - the normal error for trying to use a global name that doesn't exist.

Aggravating factors

  1. Python's default import system is based on names, not file paths, and Python does not distinguish between "(driver) script files" and "library (module) files". Any Python source code can be imported. Although Python caches module imports, the main script will not generally be in this cache, which means it is perfectly capable of attempting to import itself. This is what happens in the example in the question: since requests was not already imported, there is nothing in the cache, so the attempt in the driver script (named requests.py) to import requests will search for something to import, and find the driver script (i.e., its own source file).

    This only causes a problem at import time, if the module tries to use that import in its own initialization, e.g. by doing a from-import. Otherwise, the problem will be deferred, resulting in (typically) AttributeError or NameError when trying to use the functionality. See also:

  2. Whenever a module is loaded (i.e., imported from its source, rather than using the cache), its own top-level import statements run, causing more imports, recursively. Any of these indirect imports could potentially find the local code. The Python standard library is not a package, and mostly uses absolute import to refer to its other components. For example, as pointed out in Dave Rove's answer, attempting to import the standard library decimal module could fail when tried from a source file named numbers.py, or within a project that has such a source file.

    In one especially pernicious case, having a file named token.py in a project (or the current working directory, when starting up Python in interactive mode) causes the interactive help to break:

    $ 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)
    

    The traceback tells us all we need to know: calling help triggers a deferred import of the standard library pydoc, which indirectly attempts to import the standard library token, but finds our token.py which doesn't contain the appropriate name. In older versions of Python, it was even worse: tokenize would do a star-import from token, and then its top-level code would try to use a name defined there, resulting in NameError - and a stack trace not mentioning the file name token.py.

  3. Python can import from cached bytecode files (.pyc) even if the corresponding source file is renamed or deleted. This is intended to speed up imports, but it creates an extra step to keep code directories "clean". After renaming any .py file to resolve a problem like this, make sure to check the bytecode cache as well.

Solving the problem

Of course, the standard advice is simple and straightforward: just rename the .py files that were mistakenly imported, and delete any cached bytecode .pyc files for those modules. By default, in Python 3.x, the .pyc files for any .py files within a folder will be put into a subfolder with the special name __pycache__; in 2.x, they simply appeared alongside the corresponding .py files.

However, while this approach quickly solves the immediate problem for most people encountering it, the advice does not scale very well. There are a lot of potentially problematic names; while most projects will not be interested in including a source file named e.g. os.py, some other names might be more desirable, or harder to work around. So, here are some other useful techniques:

Controlling sys.path

Of course, since the problem is caused by sys.path specifying that absolute imports should look in the current project first, it can be avoided by just changing the sys.path.

The problematic path is described in the documentation as a "potentially unsafe path". When using the interactive Python prompt, this will be an empty string (a relative path equivalent to '.') - i.e., the current working directory for the Python process, reflecting any changes made by using e.g. os.chdir. For driver scripts started normally (python driver.py), it will be the directory where the script is located, as an absolute path (not necessarily the current working directory, since a path could be specified on the command line, like python path/to/driver.py). For modules run using the -m command-line flag, it will be the initial current working directory (not necessarily where the module is located, but also an absolute path that will not be affected by os.chdir).

To avoid having this path in sys.path, do one of the following:

  • In Python 3.11 and up, set the PYTHONSAFEPATH environment variable, or use the -P command-line option to Python.

  • In Python 3.4 and up, use the -I command-line option to start in isolated mode. This, however, has several other effects: it will ignore environment variables like PYTHONPATH and PYTHONHOME, and it will also skip adding the user-specific site-packages directory to the path (therefore the code will not have access to third-party libraries that were installed using the --user option for Pip).

  • If all else fails, consider manually manipulating sys.path. This is messy and error-prone, but sys.path is just an ordinary list of strings with file paths, and modifying its contents will affect future imports. (Do not try to replace the list object; this will not work, because Python does not look for it by the name sys.path, but uses a hard-coded internal reference. It is not possible to destroy or replace that object from Python; sys.path is just a name that is initialized to refer to it.)

Keep in mind that removing the "unsafe" path will prevent intentional absolute imports from working within the package. This is inconvenient for some small projects, but also a good reason to learn proper package organization. Speaking of which:

Using relative imports within packages

A well-organized Python project will typically consist of one or more (usually just one) packages, stored in subfolders of the main project folder, plus one or more driver scripts placed outside of the package subfolders. The driver scripts will typically use a single absolute import to access the package functionality, while implementing some simple wrapper logic (e.g. to parse command-line arguments, format and report otherwise-uncaught exceptions, etc.). The package, meanwhile, will use relative imports throughout for its own content, and absolute import only where necessary to access other packages (the standard library and third-party dependencies).

For example, a project might be organized like:

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

Code in driver.py will use absolute import into the package, like:

from my_package.x import entry_point

if __name__ == '__main__':
    entry_point()

(If logic is needed to parse the command-line arguments, it should normally go in the driver rather than in the package's code.)

Then, code inside the package will use relative imports - so x.py might contain something like:

from .y import first_thing
from .z import second_thing

def entry_point():
    first_thing()
    second_thing()

This gets the best of both worlds: the initial absolute import sets up the top-level package so that relative imports will work, and the relative imports will avoid depending on the sys.path configuration. Even without taking steps to configure sys.path, it will typically include the folder with the driver scripts, but not any package folders; thus, this also automatically avoids import path conflicts. (An absolute import won't find the package contents unless it specifies the corresponding package path; but typically when it does, importing from the current package was intentional.)

This also avoids setting traps for the next project which has this one as a dependency. Say for example that we implement and publish an API package, and someone else writes Client which has API as a dependency. Since API code will use relative import for other API functionality (say, from . import functionality), the Client project's own functionality.py won't cause a problem.

For the initial absolute import to work, of course, the top-level package folder needs to be mentioned in sys.path. However, this is normally accomplished by installing the package, so it does not cause a problem.

Disabling bytecode caching

If a file needs to be renamed, avoiding problems with .pyc files will be easier if they simply don't exist in the first place. They are not necessary, after all; they are, again, simply intended to speed up imports on subsequent runs of the program.

To suppress .pyc file generation, use the -B command-line option or set the PYTHONDONTWRITEBYTECODE environment variable. (There is no built-in solution to delete all existing .pyc files, but this is easy enough to implement by hand using e.g. the shutil standard library module.)

Linting to avoid problematic names

Consider using third-party tools (such as IDE plugins) to warn about filenames used by the standard library or by third-party libraries in the project. In Python 3.10 and up, the full list of standard library module names is also available as sys.stdlib_module_names. This includes names that might not be present in the current Python installation (e.g. OS-specific components or things that are sometimes disabled or omitted, such as Tkinter).

天涯沦落人 2025-01-26 19:20:01

只需将导致错误的导入放在最后,然后将其移至导入的底部

ex:
import requests
import flask
import numpy

如果 importflask 导致错误,

solution:-
import requests
import numpy
import flask

just put down the import which causing error to the last

ex:
import requests
import flask
import numpy

if import flask is causing error then move that to bottom of the imports

solution:-
import requests
import numpy
import flask
梦醒灬来后我 2025-01-26 19:20:01

循环依赖性:当两个或多个模块相互依赖时,就会发生。这是由于每个模块都是按照另一个模块定义的事实。

如果您获得循环导入与请求模块错误类似的错误。

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

请尝试重命名文件。此错误的错误通常是由于您试图导入 requests 模块的文件名冲突。

我也遇到了同样的问题,我的文件名是 email.py ,而我正试图导入请求模块。因此,它与 email.parser 有一些冲突。因此,我将文件名从 email.py 更改为 email1.py ,并且有效。

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

Circular dependency : It occurs when two or more modules depend on each other. This is due to the fact that each module is defined in terms of the other.

If you are getting circular import error similar to below request module error.

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

Please try to rename the file. The error for this error is usually due to conflict with the file name where you are trying to import the requests module.

I was also having same problem, where my file name was email.py and I was trying to import the requests module. So, it was having some conflict with email.parser. So, I changed the file name from email.py to email1.py and it worked.

For more info on circular dependency: https://stackabuse.com/python-circular-imports/

魄砕の薆 2025-01-26 19:20:01

问题中提到的循环依赖错误的原因在接受的答案中进行了解释:

发生这种情况是因为名为 requests.py 的本地模块遮盖了您尝试使用的已安装 requests 模块。

就我而言,我在模块中遇到了循环依赖错误,但没有具有该名称的文件或目录。

我的猜测是该模块以某种方式损坏,因为在我卸载它并重新安装相同版本后,它起作用了:

pip uninstall <module>==<version>
pip install <module>==<version>

您可以在错误日志堆栈跟踪中识别导致错误的模块。

当错误与文件名无关时,这对于收到此错误的人可能会有所帮助。

The reason for the circular dependency error mentioned in the question is explained in the accepted answer:

This happens because your local module named requests.py shadows the installed requests module you are trying to use.

In my case, I encountered a circular dependency error in a module but had no file or directory with that name.

My guess is that the module became corrupted somehow because after I uninstalled it and reinstalled the same version, it worked:

pip uninstall <module>==<version>
pip install <module>==<version>

You can identify the module causing the error in the error log stack trace.

This might be helpful for those receiving this error when it is not related to the file name.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文