python import 思考一
问题
我们能够使用模块和模块内的方法是为什么
(1) 为什么有的要导入 才能使用
(2) 为什么有的不需要导入就可以使用呢
思考
在解决这两个疑惑之前 我们需要介绍一下 sys.modules, sys.path, dir() 函数 这三个对象
sys.modules: 是一个 Python 内置的字典,它存储了已经被导入的模块。 无论这些模块是内置模块、第三方模块还是自定义模块,
一旦被导 入,它们都会被添加到 sys.modules 中。这有助于 Python 避免重复导入相同的模块,提高性能。
sys.path:是一个列表,包含了 Python 解释器在导入模块时搜索的路径。这些路径是 Python 查找模块文件的位置。
当使用 import 语句导入一个模块时, Python 会依次在 sys.path 中的每个目录下查找对应的模块文件(如 .py 文件或包目录)。
dir(): 是一个内置函数,它可以用来查看对象的属性和方法。当不带参数调用 dir() 时,它会返回当前作用域中可用的名称列表,
包括变量、函数、类和已导入模块的名称。当传入一个对象作为参数时,它会返回该对象的属性和方法列表。
由此可以初步得出:一个模块()能被当前脚本使用的关键在于其是否在当前脚本的全局作用域中,而这个全局作用域可以用内置函数dir() 查看
我们用 dir()查看一下测试文件 main.py 代码如下
import sys
print(dir())
# 输出
# ['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'sys']
可以发现除了我手动导入的sys模块外 还有许多以双下划线开始结束的魔法属性。 先说sys, 此时其被加入到了当前脚本的命名空间内,所以就可以使用其模块。
那么又有一个疑问了, 为啥我能使用print()和dir()这两个函数呢,它们又不在sys中。回答这个问题我们需要知道解释器在启动时,还没执行我们脚本代码前,会将一些python 内置模块/函数加载进解释器的运行环境中。我们不需要手动导入这些内置函数,就可以使用了。有点类似于shell 环境中可以使用内置命令(ls…)的赶脚。那么我怎么知道有哪些内置函数被加载进去了呢。哎嘿这里我们就得关注那些魔法属性了,其__buiiltins__这个魔法属性就决定了哪些内置函数被加载。我们执行下面代码:
import sys
print(__builtins__)
print(id(__builtins__), id(sys.modules['builtins']))
print(dir(__builtins__))
"""
输出
<module 'builtins' (built-in)>
2912148194064 2912148194064
['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BlockingIOError', 'BrokenPipeError', 'BufferError', 'BytesWarning', 'ChildProcessError', 'ConnectionAbortedError', 'ConnectionError', 'ConnectionRefusedError', 'ConnectionResetError', 'DeprecationWarning', 'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', 'False', 'FileExistsError', 'FileNotFoundError', 'FloatingPointError', 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning', 'IndentationError', 'IndexError', 'InterruptedError', 'IsADirectoryError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'ModuleNotFoundError', 'NameError', 'None', 'NotADirectoryError', 'NotImplemented', 'NotImplementedError', 'OSError', 'OverflowError', 'PendingDeprecationWarning', 'PermissionError', 'ProcessLookupError', 'RecursionError', 'ReferenceError', 'ResourceWarning', 'RuntimeError', 'RuntimeWarning', 'StopAsyncIteration', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'TimeoutError', 'True', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning', 'WindowsError', 'ZeroDivisionError', '__build_class__', '__debug__', '__doc__', '__import__', '__loader__', '__name__', '__package__', '__spec__', 'abs', 'all', 'any', 'ascii', 'bin', 'bool', 'breakpoint', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', 'compile', 'complex', 'copyright', 'credits', 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval', 'exec', 'exit', 'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'isinstance', 'issubclass', 'iter', 'len', 'license', 'list', 'locals', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'quit', 'range', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars', 'zip']
"""
可以看到__builtins__是一个模块内置模块对象,其实它指向的就是builtins内置模块。可以看到它们的内存ID一样,说明是同一对象。 我们再用dir()查看其明细,就能发现它所有的内置方法了
知道了模块如何才能被当前脚本识别并执行后。我们再深究一下导入机制,为什么我import sys 的时候导入的是内置模块呢,换个方式(我想导入一个模块,会有什么限制条件并且需要遵循什么机制才能导入成功呢)。这需要了解一下导入的机制了。
import module发生情况
- 会在sys.module 中查找该模块是否存在
- 如果存在,则模块不会被重新导入,会直接使用sys.module中对应的该模块(如下所例)
import sys
print(id(sys.modules['os']))
import os
print(id(sys.modules['os']))
"""
输出
2923647252496
2923647252496
"""
# os对象导入前后都是同一个,说明:os 在未手工导入之前已被导入过了,手动导入不会覆盖导入,就是之前的那个
# 同时注意虽然未手工导入之前os已存在于sys.modules中了,但此时并不存在于脚本的命名空间,所以不能使用其中的方法
# 所以我们要使用它任然要手工导入哦,印证了上述开始问题的解答
# 最后有一些内置的模块在解释器启动的开始(脚本运行之前)就已被加载进sys.modules中了。
# 哪些内置模块在解释器启动时就已经自动加载了 可以输入print(sys.modules)查看期间的 built-in模块
import sys
for item in sys.modules.items():
print(item)
"""
输出
('sys', <module 'sys' (built-in)>)
('builtins', <module 'builtins' (built-in)>)
('_frozen_importlib', <module '_frozen_importlib' (frozen)>)
('_imp', <module '_imp' (built-in)>)
('_warnings', <module '_warnings' (built-in)>)
('_frozen_importlib_external', <module '_frozen_importlib_external' (frozen)>)
('_io', <module 'io' (built-in)>)
('marshal', <module 'marshal' (built-in)>)
('nt', <module 'nt' (built-in)>)
('_thread', <module '_thread' (built-in)>)
('_weakref', <module '_weakref' (built-in)>)
('winreg', <module 'winreg' (built-in)>)
('time', <module 'time' (built-in)>)
('zipimport', <module 'zipimport' (frozen)>)
('_codecs', <module '_codecs' (built-in)>)
('codecs', <module 'codecs' from 'D:\\python\\python3.8.8\\lib\\codecs.py'>)
('encodings.aliases', <module 'encodings.aliases' from 'D:\\python\\python3.8.8\\lib\\encodings\\aliases.py'>)
('encodings', <module 'encodings' from 'D:\\python\\python3.8.8\\lib\\encodings\\__init__.py'>)
('encodings.utf_8', <module 'encodings.utf_8' from 'D:\\python\\python3.8.8\\lib\\encodings\\utf_8.py'>)
('_signal', <module '_signal' (built-in)>)
('__main__', <module '__main__' from 'E:/work/my_project/main.py'>)
('encodings.latin_1', <module 'encodings.latin_1' from 'D:\\python\\python3.8.8\\lib\\encodings\\latin_1.py'>)
('_abc', <module '_abc' (built-in)>)
('abc', <module 'abc' from 'D:\\python\\python3.8.8\\lib\\abc.py'>)
('io', <module 'io' from 'D:\\python\\python3.8.8\\lib\\io.py'>)
('_stat', <module '_stat' (built-in)>)
('stat', <module 'stat' from 'D:\\python\\python3.8.8\\lib\\stat.py'>)
('_collections_abc', <module '_collections_abc' from 'D:\\python\\python3.8.8\\lib\\_collections_abc.py'>)
('genericpath', <module 'genericpath' from 'D:\\python\\python3.8.8\\lib\\genericpath.py'>)
('ntpath', <module 'ntpath' from 'D:\\python\\python3.8.8\\lib\\ntpath.py'>)
('os.path', <module 'ntpath' from 'D:\\python\\python3.8.8\\lib\\ntpath.py'>)
('os', <module 'os' from 'D:\\python\\python3.8.8\\lib\\os.py'>)
('_sitebuiltins', <module '_sitebuiltins' from 'D:\\python\\python3.8.8\\lib\\_sitebuiltins.py'>)
('site', <module 'site' from 'D:\\python\\python3.8.8\\lib\\site.py'>)
"""
# 注意是后缀未built-in的
# 至于非内置也被导入,是因为内置模块导入时 间接导入引起的,在这也可以看到 os 模块不是内置的模块, 这里被AI坑了
3. 如果不存在,则会根据sys.path 中的文件目录查找对应的模块. sys.path 中的查找顺序如下
如果将脚本当作模块执行 (python -m src.module) 则会是执行该命令所在的路径,即pwd 显示的路径(不一定是src哦)
注意:有的python解释器版本不会将当前目录作为sys.path[0]导入,实测绿色插件版本就不行。
4.如果能在sys.path 中找到,就把对应的module加入sys.modules中,并且同时加入当前脚本的命名空间中(此后就可用命名空间内的名称调用该模块)
5.如果找不到 则会报错。
作者:Agion_n