建议19: 有节制的使用 from...import语句

python 提供三种导入外来模块的方式

  1. import xx
  2. from xx import xx
  3. __import__

在使用import时注意

  1. 尽量优先使用import a形式,访问B时使用a.B;
  2. 有节制的使用from a import B, 可以直接访问B;
  3. 尽量避免使用from a import *,这会污染命名空间,且无法清晰表示导入了哪些对象;

python中的import机制

为什么要有这三条注意呢?先来了解一下Python 中的 import 机制。
Python在初始化运行环境时会预先加载一批内建模块到内存中,这些模块相关的信息被存放在 sys.modules 中。 解释器中输入 sys.modules.items() 可显示所有预加载模块的相关信息。当加载一个模块时,解释器实际上要完成以下动作:

  1. 在 sys.modules 中进行搜索看看该模块是否已经存在,如果存在,则将其导入到当前局部命名空间,加载结束。
  2. 如果在 sys.modules 中找不到对应模块的名称,则为需要导入的模块创建一个字典对象,并将该对象信息插入 sys.modules 中。
  3. 加载前确认是否需要对模块对应的文件进行编译,如果需要则先进行编译。
  4. 执行动态加载,在当前模块的命名空间中执行编译后的字节码,并将其中所有的对象放入模块对应的字典中。

感觉有点看不懂,下面这个example会方便理解一些。

先在本地创建一个自定义模块testmodule.py

a=1
b='a'
print("test module")
>>> dir() 
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__']
>>> import testmodule  # import执行,模块同时被执行,有“test module”输出
test module
>>> dir()  # 导入testmodule之后,局部命名空间发生变化
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'testmodule']
>>> import sys
>>> 'testmodule' in sys.modules.keys()
True
>>> id(testmodule)
140620943617384
>>> id(sys.modules["testmodule"])
140620943617384
>>> dir(testmodule)   # a 和 b被写入testmodule的字典信息中
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'a', 'b']
>>>

从输出结果看:

  1. 对于用户定义的模块,import 机制会创建一个新的module将其加入当前的局部命名空间中,与此同时,sys.modules 也加入了该模块的相关信息。但从它们的id输出结果可以看出,本质上是引用同一个对象。
  2. 同时testmodule.py所在目录生成了一个.pyc文件,该文件为解释器生成的模块相对应的字节码。
  3. 从import之后的输出“test module” 可以看出模块同时被执行,而a和b被写入testmodule对应的字典信息中。

局部命名空间怎么理解。

from a import B 和 import的差异

需要注意的是,直接使用 import 和使用 from a import B 形式这两者之间存在一定的差异,后者直接将 B 暴露于当前局部空间,而将 a 加载到 sys.modules 集合。

>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__']
>>> from testmodule import b
test module
>>> dir()  # 多了一个变量b
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'b']
>>> import sys
>>> sys.modules["testmodule"]
<module 'testmodule' from '/home/disk1/jenkins/workspace/metadata_3.5_3.4_celerdata_cloud/StarRocksTest/testmodule.py'>
>>> id(sys.modules["testmodule"])   # 也会给该模块建立一个字典对象,但testmodule不在dir()局部空间中,所以也不能直接执行testmodule.a
139719442873704
>>> id(b)
139719571482304
>>> id(sys.modules["b"])   # b不是模块
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'b'

无节制使用from a import B带来的问题

1)命名空间的冲突
假设我们现在有两个模块 a 和 b,内容如下:

# cat a.py
def add():
    print("add module a")

# cat b.py
def add():
    print("add module b")

>>> from a import add
>>> from b import add
>>> add()
add module b
>>>

可以看到后导入的包中的 add() 覆盖了先导入的。特别是在一个较大的项目中若频繁的使用 from ... import ... 就会大概率的出现命名空间冲突的问题。 所以一般最好不使用该方式,若在非常明确不会导致冲突的前提在,以下情况可以考虑:

  1. 只导入少数方法或属性时;
  2. 使用 模块名.名称 的方式过于繁琐的时候;
  3. 模块的文档明确说明需要使用from...import形式,导人的是一个包下面的子模块, 且使用from...import形式能够更为简单和便利时。如使用from io.drivers import zip要比使用 import io.drivers.zip更方便。

2)循环嵌套导入的问题

附加

Python 2.7.5,有42个预加载模块

Python 2.7.5 (default, Jun 20 2023, 11:36:40)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-44)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> for each_module in sys.modules.items():
...     print(each_module)
...
('google', <module 'google' (built-in)>)
('copy_reg', <module 'copy_reg' from '/usr/lib64/python2.7/copy_reg.pyc'>)
('sre_compile', <module 'sre_compile' from '/usr/lib64/python2.7/sre_compile.pyc'>)
('_sre', <module '_sre' (built-in)>)
('encodings', <module 'encodings' from '/usr/lib64/python2.7/encodings/__init__.pyc'>)
('site', <module 'site' from '/usr/lib64/python2.7/site.pyc'>)
('__builtin__', <module '__builtin__' (built-in)>)
('sysconfig', <module 'sysconfig' from '/usr/lib64/python2.7/sysconfig.pyc'>)
('__main__', <module '__main__' (built-in)>)
('encodings.encodings', None)
('abc', <module 'abc' from '/usr/lib64/python2.7/abc.pyc'>)
('posixpath', <module 'posixpath' from '/usr/lib64/python2.7/posixpath.pyc'>)
('_weakrefset', <module '_weakrefset' from '/usr/lib64/python2.7/_weakrefset.pyc'>)
('errno', <module 'errno' (built-in)>)
('encodings.codecs', None)
('sre_constants', <module 'sre_constants' from '/usr/lib64/python2.7/sre_constants.pyc'>)
('re', <module 're' from '/usr/lib64/python2.7/re.pyc'>)
('_abcoll', <module '_abcoll' from '/usr/lib64/python2.7/_abcoll.pyc'>)
('types', <module 'types' from '/usr/lib64/python2.7/types.pyc'>)
('_codecs', <module '_codecs' (built-in)>)
('encodings.__builtin__', None)
('_warnings', <module '_warnings' (built-in)>)
('genericpath', <module 'genericpath' from '/usr/lib64/python2.7/genericpath.pyc'>)
('stat', <module 'stat' from '/usr/lib64/python2.7/stat.pyc'>)
('zipimport', <module 'zipimport' (built-in)>)
('_sysconfigdata', <module '_sysconfigdata' from '/usr/lib64/python2.7/_sysconfigdata.pyc'>)
('warnings', <module 'warnings' from '/usr/lib64/python2.7/warnings.pyc'>)
('UserDict', <module 'UserDict' from '/usr/lib64/python2.7/UserDict.pyc'>)
('encodings.utf_8', <module 'encodings.utf_8' from '/usr/lib64/python2.7/encodings/utf_8.pyc'>)
('sys', <module 'sys' (built-in)>)
('codecs', <module 'codecs' from '/usr/lib64/python2.7/codecs.pyc'>)
('readline', <module 'readline' from '/usr/lib64/python2.7/lib-dynload/readline.so'>)
('os.path', <module 'posixpath' from '/usr/lib64/python2.7/posixpath.pyc'>)
('signal', <module 'signal' (built-in)>)
('traceback', <module 'traceback' from '/usr/lib64/python2.7/traceback.pyc'>)
('linecache', <module 'linecache' from '/usr/lib64/python2.7/linecache.pyc'>)
('posix', <module 'posix' (built-in)>)
('encodings.aliases', <module 'encodings.aliases' from '/usr/lib64/python2.7/encodings/aliases.pyc'>)
('exceptions', <module 'exceptions' (built-in)>)
('sre_parse', <module 'sre_parse' from '/usr/lib64/python2.7/sre_parse.pyc'>)
('os', <module 'os' from '/usr/lib64/python2.7/os.pyc'>)
('_weakref', <module '_weakref' (built-in)>)
>>> len(sys.modules.items())
42

Python 3.6.8,有个预加载模块

Python 3.6.8 (default, Jun 20 2023, 11:53:23)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-44)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> for each_module in sys.modules.items():
...     print(each_module)
...
('builtins', <module 'builtins' (built-in)>)
('sys', <module 'sys' (built-in)>)
('_frozen_importlib', <module 'importlib._bootstrap' (frozen)>)
('_imp', <module '_imp' (built-in)>)
('_warnings', <module '_warnings' (built-in)>)
('_thread', <module '_thread' (built-in)>)
('_weakref', <module '_weakref' (built-in)>)
('_frozen_importlib_external', <module 'importlib._bootstrap_external' (frozen)>)
('_io', <module 'io' (built-in)>)
('marshal', <module 'marshal' (built-in)>)
('posix', <module 'posix' (built-in)>)
('zipimport', <module 'zipimport' (built-in)>)
('encodings', <module 'encodings' from '/usr/lib64/python3.6/encodings/__init__.py'>)
('codecs', <module 'codecs' from '/usr/lib64/python3.6/codecs.py'>)
('_codecs', <module '_codecs' (built-in)>)
('encodings.aliases', <module 'encodings.aliases' from '/usr/lib64/python3.6/encodings/aliases.py'>)
('encodings.utf_8', <module 'encodings.utf_8' from '/usr/lib64/python3.6/encodings/utf_8.py'>)
('_signal', <module '_signal' (built-in)>)
('__main__', <module '__main__' (built-in)>)
('encodings.latin_1', <module 'encodings.latin_1' from '/usr/lib64/python3.6/encodings/latin_1.py'>)
('io', <module 'io' from '/usr/lib64/python3.6/io.py'>)
('abc', <module 'abc' from '/usr/lib64/python3.6/abc.py'>)
('_weakrefset', <module '_weakrefset' from '/usr/lib64/python3.6/_weakrefset.py'>)
('site', <module 'site' from '/usr/lib64/python3.6/site.py'>)
('os', <module 'os' from '/usr/lib64/python3.6/os.py'>)
('errno', <module 'errno' (built-in)>)
('stat', <module 'stat' from '/usr/lib64/python3.6/stat.py'>)
('_stat', <module '_stat' (built-in)>)
('posixpath', <module 'posixpath' from '/usr/lib64/python3.6/posixpath.py'>)
('genericpath', <module 'genericpath' from '/usr/lib64/python3.6/genericpath.py'>)
('os.path', <module 'posixpath' from '/usr/lib64/python3.6/posixpath.py'>)
('_collections_abc', <module '_collections_abc' from '/usr/lib64/python3.6/_collections_abc.py'>)
('_sitebuiltins', <module '_sitebuiltins' from '/usr/lib64/python3.6/_sitebuiltins.py'>)
('sysconfig', <module 'sysconfig' from '/usr/lib64/python3.6/sysconfig.py'>)
('_sysconfigdata_m_linux_x86_64-linux-gnu', <module '_sysconfigdata_m_linux_x86_64-linux-gnu' from '/usr/lib64/python3.6/_sysconfigdata_m_linux_x86_64-linux-gnu.py'>)
('_bootlocale', <module '_bootlocale' from '/usr/lib64/python3.6/_bootlocale.py'>)
('_locale', <module '_locale' (built-in)>)
('types', <module 'types' from '/usr/lib64/python3.6/types.py'>)
('functools', <module 'functools' from '/usr/lib64/python3.6/functools.py'>)
('_functools', <module '_functools' (built-in)>)
('collections', <module 'collections' from '/usr/lib64/python3.6/collections/__init__.py'>)
('operator', <module 'operator' from '/usr/lib64/python3.6/operator.py'>)
('_operator', <module '_operator' (built-in)>)
('keyword', <module 'keyword' from '/usr/lib64/python3.6/keyword.py'>)
('heapq', <module 'heapq' from '/usr/lib64/python3.6/heapq.py'>)
('_heapq', <module '_heapq' from '/usr/lib64/python3.6/lib-dynload/_heapq.cpython-36m-x86_64-linux-gnu.so'>)
('itertools', <module 'itertools' (built-in)>)
('reprlib', <module 'reprlib' from '/usr/lib64/python3.6/reprlib.py'>)
('_collections', <module '_collections' (built-in)>)
('weakref', <module 'weakref' from '/usr/lib64/python3.6/weakref.py'>)
('collections.abc', <module 'collections.abc' from '/usr/lib64/python3.6/collections/abc.py'>)
('importlib', <module 'importlib' from '/usr/lib64/python3.6/importlib/__init__.py'>)
('importlib._bootstrap', <module 'importlib._bootstrap' (frozen)>)
('importlib._bootstrap_external', <module 'importlib._bootstrap_external' (frozen)>)
('warnings', <module 'warnings' from '/usr/lib64/python3.6/warnings.py'>)
('importlib.util', <module 'importlib.util' from '/usr/lib64/python3.6/importlib/util.py'>)
('importlib.abc', <module 'importlib.abc' from '/usr/lib64/python3.6/importlib/abc.py'>)
('importlib.machinery', <module 'importlib.machinery' from '/usr/lib64/python3.6/importlib/machinery.py'>)
('contextlib', <module 'contextlib' from '/usr/lib64/python3.6/contextlib.py'>)
('acsclient', <module 'acsclient' (namespace)>)
('mpl_toolkits', <module 'mpl_toolkits' (namespace)>)
('google', <module 'google' (namespace)>)
('google.logging', <module 'google.logging' (namespace)>)
('snowflake', <module 'snowflake' (namespace)>)
('encodings.cp437', <module 'encodings.cp437' from '/usr/lib64/python3.6/encodings/cp437.py'>)
('readline', <module 'readline' from '/usr/lib64/python3.6/lib-dynload/readline.cpython-36m-x86_64-linux-gnu.so'>)
('atexit', <module 'atexit' (built-in)>)
('rlcompleter', <module 'rlcompleter' from '/usr/lib64/python3.6/rlcompleter.py'>)
>>> len(sys.modules.items())
68

posted on 2025-05-16 16:10  Y.Debby  阅读(26)  评论(0)    收藏  举报