如果文件名为 email.py 或 logging.py,则无法使用 Python 请求模块?

Cannot use Python Requests module if file is named email.py or logging.py?

我有一个简单的测试脚本:

import requests
response = requests.get('http://httpbin.org/get')
print response.text

当 python 脚本命名为 test.py 时有效,但如果命名为 email.pylogging.py:

则失败
Traceback (most recent call last):
  File "./email.py", line 3, in <module>
    import requests
  File "/usr/lib/python2.7/dist-packages/requests/__init__.py", line 53, in <module>
    from urllib3.contrib import pyopenssl
  File "/usr/lib/python2.7/dist-packages/urllib3/__init__.py", line 16, in <module>
    from .connectionpool import (
  File "/usr/lib/python2.7/dist-packages/urllib3/connectionpool.py", line 59, in <module>
    from .request import RequestMethods
  File "/usr/lib/python2.7/dist-packages/urllib3/request.py", line 12, in <module>
    from .filepost import encode_multipart_formdata
  File "/usr/lib/python2.7/dist-packages/urllib3/filepost.py", line 15, in <module>
    from .fields import RequestField
  File "/usr/lib/python2.7/dist-packages/urllib3/fields.py", line 7, in <module>
    import email.utils
  File "/home/ubuntu/temp/email.py", line 4, in <module>
    response = requests.get('http://httpbin.org/get')
AttributeError: 'module' object has no attribute 'get'

似乎 requests 导入了 urllib3,它导入了 email 内置模块。为什么 Python 找不到 the built-in email module first,而不是在当前路径中查找 email.py

有没有办法让它工作,或者我是否只需要始终避免将我的 Python 脚本命名为任何可能由任何依赖项导入的内置模块?

这不是请求的问题。

"problem" 来自 Python。 emaillogging是标准模块,除非你理解并愿意解决冲突,否则不建议将自己的模块命名为相同的。

首先,正如您在评论中指出的那样,Python 检查具有该名称的 'built-in' 模块。并非标准库中的所有模块都是 'built-in'。您可以通过以下方式查看列表:

print sys.builtin_module_names

如果在那里找不到,搜索的顺序由 Burhan 在此处接受的答案概述:

What is the extent of the import statement in Python

Python searches for things it can import in the following order:

  • From the directory where the script was executed.
  • From the directories in the PYTHONPATH environment variable (if its set).
  • From the system-wide Python installation's module directory.

对于您的情况,电子邮件不是内置的,因此首先检查当前目录。

所以,是的,不要隐藏 python 库名称。从技术上讲,您可以隐藏内置模块的名称,但请不要这样做。让人Python哭泣。

看看 sys.path,您会看到 '' 作为第一个条目:

>>> import sys
>>> sys.path
['',
 '/.../3.3/lib/python33.zip',
 '/.../3.3/lib/python3.3',
 '/.../3.3/lib/python3.3/plat-darwin',
 '/.../3.3/lib/python3.3/lib-dynload',
 '/.../3.3/lib/python3.3/site-packages']

''就是当前目录。

您可以修改 sys.path,但从长远来看,不给 python 匹配内置模块的文件名是一个更明智的决定。

Python 首先在当前目录中查找模块的原因是 Python 就是这样工作的。如果您查看 sys.path,您会发现它的第一个元素几乎总是 '',表示当前目录。这适用于除内置模块之外的所有内容,内置模块在解释器启动时为 "imported"。 (它们并没有真正导入,只是分配到 sys.modules。)

一般来说,您不应将自己的模块命名为与您正在使用的其他模块相同的名称(无论它们是否带有 Python)。我倾向于将我的首字母写在我正在使用的脚本上,只是为了避免这个陷阱。

当然,您可以直接操作 sys.path,这样它就不会在当前目录中查找模块:

import sys
if not sys.path[0]:
   del sys.path[0]
import requests

请注意,您只需要在导入时删除 '' 条目 您应该在导入后将其放回原处,以防您需要导入从您自己的脚本目录中删除同名模块。上下文管理器对此很方便。

import sys
from contextlib import contextmanager

@contextmanager
def no_cwd_imports():
    old_path = sys.path[:]
    if not sys.path[0]:
        del sys.path[0]
    try:
        yield
    finally:
        sys.path[:] = old_path

with no_cwd_imports():
    import requests