<blockquote>
<h1>How to use python's <code>__import__()</code> function properly?</h1>
</blockquote>
<p>有两种用途:</p>
<ul>
<li>直接进口</li>
<li>改变导入行为的钩子</li>
</ul>
<p>大部分情况下,你也不需要这么做。</p>
<h2>用于用户空间导入</h2>
<p>最佳实践是改用<code>importlib</code>。但如果你坚持:</p>
<p>一般用法:</p>
<pre><code>>>> sys = __import__('sys')
>>> sys
<module 'sys' (built-in)>
</code></pre>
<p>复杂:</p>
<pre><code>>>> os = __import__('os.path')
>>> os
<module 'os' from '/home/myuser/anaconda3/lib/python3.6/os.py'>
>>> os.path
<module 'posixpath' from '/home/myuser/anaconda3/lib/python3.6/posixpath.py'>
</code></pre>
<p>如果需要名称中最右边的子模块,请将非空列表(例如<code>[None]</code>)传递给<code>fromlist</code>:</p>
<pre><code>>>> path = __import__('os.path', fromlist=[None])
>>> path
<module 'posixpath' from '/home/myuser/anaconda3/lib/python3.6/posixpath.py'>
</code></pre>
<p>或者,如文档所述,使用<code>importlib.import_module</code>:</p>
<pre><code>>>> importlib = __import__('importlib')
>>> futures = importlib.import_module('concurrent.futures')
>>> futures
<module 'concurrent.futures' from '/home/myuser/anaconda3/lib/python3.6/concurrent/futures/__init__.py'>
</code></pre>
<h3>文件</h3>
<p><code>__import__</code>的文档是最令人困惑的内置函数。</p>
<pre><code>__import__(...)
__import__(name, globals=None, locals=None, fromlist=(), level=0) -> module
Import a module. Because this function is meant for use by the Python
interpreter and not for general use it is better to use
importlib.import_module() to programmatically import a module.
The globals argument is only used to determine the context;
they are not modified. The locals argument is unused. The fromlist
should be a list of names to emulate ``from name import ...'', or an
empty list to emulate ``import name''.
When importing a module from a package, note that __import__('A.B', ...)
returns package A when fromlist is empty, but its submodule B when
fromlist is not empty. Level is used to determine whether to perform
absolute or relative imports. 0 is absolute while a positive number
is the number of parent directories to search relative to the current module.
</code></pre>
<p>如果您仔细阅读它,就会感觉到API最初的目的是允许从模块延迟加载函数。然而,这不是CPython的工作方式,我不知道是否有其他Python实现成功做到了这一点。</p>
<p>相反,CPython在第一次导入时执行模块名称空间中的所有代码,然后将模块缓存在<code>sys.modules</code>中。</p>
<p><code>__import__</code>仍然有用。但是基于文档理解它的功能是相当困难的。</p>
<h3>完全使用<code>__import__</code></h3>
<p>为了调整全部功能以演示当前的<code>__import__</code>API,这里有一个包装函数,它有一个更干净、更具文档记录的API。</p>
<pre><code>def importer(name, root_package=False, relative_globals=None, level=0):
""" We only import modules, functions can be looked up on the module.
Usage:
from foo.bar import baz
>>> baz = importer('foo.bar.baz')
import foo.bar.baz
>>> foo = importer('foo.bar.baz', root_package=True)
>>> foo.bar.baz
from .. import baz (level = number of dots)
>>> baz = importer('baz', relative_globals=globals(), level=2)
"""
return __import__(name, locals=None, # locals has no use
globals=relative_globals,
fromlist=[] if root_package else [None],
level=level)
</code></pre>
<p>要演示,例如从姐妹套餐到baz:</p>
<pre><code>baz = importer('foo.bar.baz')
foo = importer('foo.bar.baz', root_package=True)
baz2 = importer('bar.baz', relative_globals=globals(), level=2)
assert foo.bar.baz is baz is baz2
</code></pre>
<h3>动态访问模块中的名称</h3>
<p>要按名称从baz模块动态访问globals,请使用<code>getattr</code>。例如:</p>
<pre><code>for name in dir(baz):
print(getattr(baz, name))
</code></pre>
<h2>挂钩以更改导入行为</h2>
<p>您可以使用<code>__import__</code>来更改或拦截导入行为。在这种情况下,我们只需打印它得到的参数来演示我们正在拦截它:</p>
<pre><code>old_import = __import__
def noisy_importer(name, locals, globals, fromlist, level):
print(f'name: {name!r}')
print(f'fromlist: {fromlist}')
print(f'level: {level}')
return old_import(name, locals, globals, fromlist, level)
import builtins
builtins.__import__ = noisy_importer
</code></pre>
<p>现在,当您导入时,您可以看到这些重要的参数。</p>
<pre><code>>>> from os.path import join as opj
name: 'os.path'
fromlist: ('join',)
level: 0
>>> opj
<function join at 0x7fd08d882618>
</code></pre>
<p>在这种情况下,获取全局或局部可能会很有用,但不会立即想到这方面的具体用途。</p>