为什么某些文件名不允许用于本地Python源代码文件?
为什么某些文件名不允许用于本地Python源代码文件?
似乎有一些文件名不能与我运行Python源代码的同一目录中的文件同名,因为它们可能与我导入的模块中的同名文件冲突。\n有没有办法保持名称不变但避免异常?\n考虑到Python已经存在了这么久,这个问题似乎很奇怪,而且默认安装的Python也没有避免这个问题,无论导入了哪些模块(例如在copy.py场景中导入了openpyxl,在email.py场景中导入了smtplib)。\n下面是两个示例异常。我的源代码在testing.py文件中。copy.py和email.py文件与testing.py文件在同一目录中。目前,我将它们重命名为copy2.py和email2.py,但这似乎是一个不好的解决方法。\ncopy.py\n
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, inmain() 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 import openpyxl # openpyxl 3.0.7 File "C:\Python\venv\Python37_64\lib\site-packages\openpyxl\__init__.py", line 6, in from openpyxl.workbook import Workbook File "C:\Python\venv\Python37_64\lib\site-packages\openpyxl\workbook\__init__.py", line 4, in from .workbook import Workbook File "C:\Python\venv\Python37_64\lib\site-packages\openpyxl\workbook\workbook.py", line 4, in from copy import copy File "C:\GitLab\p01\copy.py", line 2 def parse_slash_copy(const char *args): ^ IndentationError: unexpected indent
\nemail.py\n
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, inmain() 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 import requests File "C:\Python\venv\Python37_64\lib\site-packages\requests\__init__.py", line 43, in import urllib3 File "C:\Python\venv\Python37_64\lib\site-packages\urllib3\__init__.py", line 11, in from . import exceptions File "C:\Python\venv\Python37_64\lib\site-packages\urllib3\exceptions.py", line 3, in 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 import email.parser File "C:\GitLab\p01\email.py", line 1, in import smtplib File "C:\Python\Python37_64\lib\smtplib.py", line 47, in import email.utils ModuleNotFoundError: No module named 'email.utils'; 'email' is not a package
为什么某些文件名不允许用于本地Python源代码文件?
Python内置了许多模块,例如json、collections、copy和email。可以在官方文档中找到一个完整的列表。
由于模块/包名称在每个解释器会话中是全局唯一的,因此使用与内置模块相同名称的自定义模块/包将使后者无法通过给定名称访问。这意味着任何试图访问内置模块的代码(无论是你自己的代码还是第三方代码)都很可能失败。
对此没有解决方法。除非你有意要替换它们,否则不要重复使用内置模块/包的名称。这包括删除带有此类命名冲突的残留代码。要么重命名冲突的文件,要么将它们移到搜索路径之外。
这是不完整的。了解Python的导入机制对于解决这类问题非常重要;特别是它是如何搜索模块的。此外,在“删除残留代码”时,您还可能需要清理本地__pycache__文件夹中的缓存.pyc文件。
你知道一个好的参考资料吗?我认为当主要信息是“不要”的时候,不需要了解导入系统的所有细节。
为什么一些文件名在本地Python源代码文件中是不允许的?
在Python的默认配置中,当Python加载模块时,它会在一系列路径中寻找模块,这些路径定义在一个列表中,可以通过`sys.path`访问。
根据文档:
“在程序启动时初始化时,列表中的第一项`path[0]`是包含用于调用Python解释器的脚本的目录。如果脚本目录不可用(例如,如果解释器是以交互方式调用的,或者脚本是从标准输入中读取的),`path[0]`是空字符串,这将指导Python首先在当前目录中搜索模块。请注意,脚本目录插入在由`PYTHONPATH`导致的条目之前。”
在你的情况下,`path[0]`是`C:\GitLab\p01`,这绝对不是搜索标准库模块的地方。
个人而言,我对这个设计决策并不是很满意(我更喜欢,特别是新程序员被迫尽快理解相对导入),但这是一个有意识的设计决策,它为我们提供了很多方便。
你可以简单地修改`sys.path`来避免这个异常:
import sys local = sys.path.pop(0) import requests sys.path.insert(0, local)
这可能与程序中的其他任何对`sys.path`的修改不兼容(但至少你可以确保在问题导致的导入之后立即恢复更改)。对于每个与你想要的名称冲突的导入(乐观地说,是一系列导入),你都将被困在这样的情况中。当然,如果你想使用名称`sys`,那就没有办法了。
你可以以多种方式封装这个方法,并以多种方式插入到导入系统中。这是一个广泛的研究课题,超出了Stack Overflow答案的范围。
我同意这种设计是糟糕的,但却是被设计成这样的方式。我会给你们两个人点赞,但我会将你的答案标记为正确的。我的问题本来就有点特殊。