我在目录中创建了两个新文件random.py和main.py。代码如下:
# random.py
if __name__ == "__main__":
print("random")
# main.py
import random
if __name__ == "__main__":
print(random.choice([1, 2, 3]))
当我运行main.py文件时,程序报告错误。
Traceback (most recent call last):
File "main.py", line 8, in <module>
print(random.choice([1, 2, 3]))
AttributeError: module 'random' has no attribute 'choice'
Main.py导入我自己定义的随机模块。
但是,如果我在同一目录中创建一个新的sys.py文件和一个main.py文件,则代码如下:
# sys.py
if __name__ == "__main__":
print("sys")
# main.py
import sys
if __name__ == "__main__":
print(sys.path)
当我运行main.py文件时,成功。
main.py导入内置模块sys。
为什么会有如此明显的区别?
脚本文件的目录关系如下:
C:.
main.py
random.py
sys.py
非常感谢您的回答。 原谅我可怜的英语。
答案 0 :(得分:4)
sys
是一个内置模块,这意味着它直接编译成Python可执行文件本身。当Python寻找模块时,内置模块优先于外部文件。标准random
模块不是内置的,因此无法得到这种处理。
引用docs:
在
sys.modules
中找不到命名模块时,Python接下来将搜索sys.meta_path
,其中包含元路径查找器对象列表。 按顺序查询这些查找器,以了解他们是否知道如何处理命名模块...Python的默认
sys.meta_path
具有三个元路径查找器,一个知道如何导入内置模块,一个知道如何导入冻结的模块,另一个知道如何从导入路径导入模块(即基于路径的查找器)。
由于内置模块的查找器位于搜索导入路径的查找器之前,因此将在导入路径上的任何内容之前找到内置模块。
您可以在sys.builtin_module_names
中看到Python内置的所有模块的名称的元组。
也就是说,尽管任何内置模块都将优先于从文件加载的模块,但是sys
有其自己的特殊处理方式。 sys
是Python的基础构建块之一,sys
模块的许多设置需要在导入系统运行正常之前足以完成。 sys
是在解释器设置过程中通过绕过常规导入系统的方式显式创建的,然后以后sys
的导入在sys.modules
中找到它而不会遇到任何元路径查找器。
创建sys
的方式和位置是一个实现细节,该实现细节因Python版本而异(并且在不同的Python实现中差异很大),但是在CPython 3.7.4代码中,您可以看到它的开始在Python/pylifecycle.c
中的755行上。
答案 1 :(得分:3)
tl;博士缓存
sys
在其他python模块中有些特殊情况,因为它是在程序启动时无条件加载的(大概是因为其中的许多常量,函数和数据-例如流{{1} }和stdout
-由python解释器使用)。正如@ user2357112在另一个答案中指出的,这部分是因为它内置在python可执行文件中,而且还因为运行大量python的核心功能是必要的(请参阅下面的如何加载才能使导入工作) 。 stderr
是标准库的一部分,但执行时不会自动加载,这是我们与random
之间的主要相关差异
查看python's documentation on the subject可以阐明python如何解决导入问题:
在导入搜索期间检查的第一位是
sys
。此映射充当所有先前导入的模块(包括中间路径)的缓存。
...
导入期间,将在sys.modules
中查找模块名称,如果存在,则关联的值是满足导入要求的模块,然后过程完成。但是,如果值为sys.modules
,则会引发None
。如果缺少模块名称,Python将继续搜索模块。
关于它在哪里寻找模块,您可以从观察到的行为中看到它首先在本地目录中寻找。也就是说,它首先搜索本地目录,然后搜索“通常的位置”。
如何处理ModuleNotFoundError
与如何处理sys
之间存在差异的原因是缓存-random
被缓存了(因此python甚至不检查导入路径),而sys
未缓存(因此python会检查导入路径,并在本地导入)。
有几种方法可以更改此行为。
首先,如果必须有一个名为random
的本地模块,则可以use importlib
to import it in relative or absolute terms,而不会与已经缓存的sys
产生歧义。我不知道这会对独立尝试导入sys
的其他模块产生怎样的影响,而且您实际上不应该将文件命名为与标准库模块相同。
或者,如果您希望代码在检查本地目录之前检查python的内置模块,那么您应该能够通过修改sys
来做到这一点,该命令显示了搜索路径以查找输入的顺序(与sys.path
环境变量相同,或任何其他类似特定于语言的环境变量)。 $PATH
的第一个元素通常是一个空字符串sys.path
,这将导致搜索当前工作目录。因此,您只需将其移动到''
的后面,即可最后搜索而不是首先搜索:
sys.path