为什么本地 Python 源代码文件不允许某些文件名?

Why are certain file names not allowed for local Python source code files?

似乎有些文件名不允许存在于我的 运行 我的 Python 源代码所在的同一目录中,因为它们可能与同名文件冲突在我导入的模块中。

有没有办法保持名称不变但避免异常?

这似乎很奇怪 Python 已经存在了多长时间并且没有修复,或者 Python 的默认安装并没有避免这种情况,尽管导入了哪些模块(即:copy.py 场景中的 openpyxl 和 email.py 场景中的 smtplib)。

下面是两个示例异常。我的源代码在 testing.py 文件中。文件 copy.py 和 email.py 与 testing.py 在同一目录中。现在,我将它们重命名为 copy2.py 和 email2.py,但这似乎是一个糟糕的解决方法。

copy.py

Traceback (most recent call last):
  File "C:\Python\Python37_64\lib\runpy.py", line 193, in _run_module_as_main
    "__main__", mod_spec)
  File "C:\Python\Python37_64\lib\runpy.py", line 85, in _run_code
    exec(code, run_globals)
  File "C:\Python\Python37_64\lib\cProfile.py", line 185, in <module>
    main()
  File "C:\Python\Python37_64\lib\cProfile.py", line 178, in main
    runctx(code, globs, None, options.outfile, options.sort)
  File "C:\Python\Python37_64\lib\cProfile.py", line 20, in runctx
    filename, sort)
  File "C:\Python\Python37_64\lib\profile.py", line 62, in runctx
    prof.runctx(statement, globals, locals)
  File "C:\Python\Python37_64\lib\cProfile.py", line 100, in runctx
    exec(cmd, globals, locals)
  File "C:\GitLab\redcap-p01-etl\testing.py", line 7, in <module>
    import openpyxl # openpyxl 3.0.7
  File "C:\Python\venv\Python37_64\lib\site-packages\openpyxl\__init__.py", line 6, in <module>
    from openpyxl.workbook import Workbook
  File "C:\Python\venv\Python37_64\lib\site-packages\openpyxl\workbook\__init__.py", line 4, in <module>
    from .workbook import Workbook
  File "C:\Python\venv\Python37_64\lib\site-packages\openpyxl\workbook\workbook.py", line 4, in <module>
    from copy import copy
  File "C:\GitLab\p01\copy.py", line 2
    def parse_slash_copy(const char *args):
    ^
IndentationError: unexpected indent

email.py

Traceback (most recent call last):
  File "C:\Python\Python37_64\lib\runpy.py", line 193, in _run_module_as_main
    "__main__", mod_spec)
  File "C:\Python\Python37_64\lib\runpy.py", line 85, in _run_code
    exec(code, run_globals)
  File "C:\Python\Python37_64\lib\cProfile.py", line 185, in <module>
    main()
  File "C:\Python\Python37_64\lib\cProfile.py", line 178, in main
    runctx(code, globs, None, options.outfile, options.sort)
  File "C:\Python\Python37_64\lib\cProfile.py", line 20, in runctx
    filename, sort)
  File "C:\Python\Python37_64\lib\profile.py", line 62, in runctx
    prof.runctx(statement, globals, locals)
  File "C:\Python\Python37_64\lib\cProfile.py", line 100, in runctx
    exec(cmd, globals, locals)
  File "C:\GitLab\p01\testing.py", line 13, in <module>
    import requests
  File "C:\Python\venv\Python37_64\lib\site-packages\requests\__init__.py", line 43, in <module>
    import urllib3
  File "C:\Python\venv\Python37_64\lib\site-packages\urllib3\__init__.py", line 11, in <module>
    from . import exceptions
  File "C:\Python\venv\Python37_64\lib\site-packages\urllib3\exceptions.py", line 3, in <module>
    from .packages.six.moves.http_client import IncompleteRead as httplib_IncompleteRead
  File "C:\Python\venv\Python37_64\lib\site-packages\urllib3\packages\six.py", line 199, in load_module
    mod = mod._resolve()
  File "C:\Python\venv\Python37_64\lib\site-packages\urllib3\packages\six.py", line 113, in _resolve
    return _import_module(self.mod)
  File "C:\Python\venv\Python37_64\lib\site-packages\urllib3\packages\six.py", line 82, in _import_module
    __import__(name)
  File "C:\Python\Python37_64\lib\http\client.py", line 71, in <module>
    import email.parser
  File "C:\GitLab\p01\email.py", line 1, in <module>
    import smtplib
  File "C:\Python\Python37_64\lib\smtplib.py", line 47, in <module>
    import email.utils
ModuleNotFoundError: No module named 'email.utils'; 'email' is not a package

Python 附带许多模块,例如 jsoncollectionscopyemail.可以找到完整列表 in the official documentation.

由于 module/package 名称在每个解释器会话中是全局唯一的,因此使用与内置模块同名的自定义 modules/packages 会使后者无法通过给定名称访问。这意味着任何试图访问内置模块的代码——无论是您自己的代码还是 third-party 代码——都可能会失败。

对此没有解决方法。不要 re-use 内置 modules/packages 的名称,除非你有意要替换它们。这包括删除具有此类命名冲突的剩余代码。重命名冲突文件,或将它们移出 searched paths.

This seems bizarre being how long Python has been around and is not fixed or the default install of Python doesn't avoid this, despite what modules are imported

它没有被修复或避免,因为它被认为是按预期工作的。在默认配置中,任何时候 Python 加载模块,它都会在各种路径中查找模块,定义在一个列表中,该列表可访问为 sys.path.

来自文档:

As initialized upon program startup, the first item of this list, path[0], is the directory containing the script that was used to invoke the Python interpreter. If the script directory is not available (e.g. if the interpreter is invoked interactively or if the script is read from standard input), path[0] is the empty string, which directs Python to search modules in the current directory first. Notice that the script directory is inserted before the entries inserted as a result of PYTHONPATH.

在你的情况下,path[0]C:\GitLab\p01,这绝对不是寻找标准库模块的地方。

就个人而言,我对设计决定不是很满意(我特别希望新程序员被迫尽快了解相关导入),但这是一个有意识的设计决定,而且带来不少便利。

Is there a way to still keep the names the same but avoid the exception?

如果你坚持 - 你可以简单地破解 sys.path:

import sys
local = sys.path.pop(0)
import requests
sys.path.insert(0, local)

这不一定与 任何其他 黑客攻击兼容,您可能会尝试 sys.path 程序中的其他任何地方(但至少您可以确保更改是otherwise-problematic 导入后立即恢复)。对于与您想要的名称冲突的每次导入(乐观地,导入序列),您都会遇到这样的事情。当然,如果您想使用 sys 这个名字,那您就不走运了。

你可以用多种方式来总结它;您可以通过多种方式连接到导入系统。可以(但可能非常危险)进行动态导入(即从包含 URI 的字符串),并且可以更改 import 使用的机制。这是一个广泛的研究主题,超出了 Stack Overflow 答案的范围。