如何正确使用python的导入函数__import __()

jdb*_*org 43 python import

我正在尝试from foo.bar import object使用该__import__功能进行复制,而我似乎已经碰壁了.

from glob import glob很容易: glob = __import__("glob",glob)glob = __import__("glob").glob

我遇到的问题是我从包中导入(即bar),我希望包中的脚本成为导入源.

所以我喜欢的是类似的东西

string_to_import = "bar"
object = __import__("foo",string_to_import).object
Run Code Online (Sandbox Code Playgroud)

但这只是__init__在foo包中导入.

如何才能做到这一点?

编辑:当我使用明显的,只有__init__被称为

__import__("foo.bar")
<module 'foo' from 'foo/__init__.pyc'>
Run Code Online (Sandbox Code Playgroud)

Aar*_*all 41

如何__import__()正确使用python的功能?

有两种用途:

  • 直接进口
  • 一个改变导入行为的钩子

在大多数情况下,你真的不需要做任何一件事.

用于用户空间导入

最佳做法是改为使用importlib.但如果你坚持:

琐碎的用法:

>>> sys = __import__('sys')
>>> sys
<module 'sys' (built-in)>
Run Code Online (Sandbox Code Playgroud)

复杂:

>>> 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'>
Run Code Online (Sandbox Code Playgroud)

如果你想在名称中最右边的子模块,通过一个非空目录,例如[None],到fromlist:

>>> path = __import__('os.path', fromlist=[None])
>>> path
<module 'posixpath' from '/home/myuser/anaconda3/lib/python3.6/posixpath.py'>
Run Code Online (Sandbox Code Playgroud)

或者,如文档所声明,使用importlib.import_module:

>>> importlib = __import__('importlib')
>>> futures = importlib.import_module('concurrent.futures')
>>> futures
<module 'concurrent.futures' from '/home/myuser/anaconda3/lib/python3.6/concurrent/futures/__init__.py'>
Run Code Online (Sandbox Code Playgroud)

文档

文档__import__是内置函数中最容易混淆的.

__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.
Run Code Online (Sandbox Code Playgroud)

如果仔细阅读,您会发现API最初旨在允许从模块中延迟加载函数.但是,这不是CPython的工作方式,而且我不知道Python的任何其他实现是否设法做到了这一点.

相反,CPython在第一次导入时执行模块命名空间中的所有代码,之后模块被缓存sys.modules.

__import__仍然有用.但是根据文档理解它的作用是相当困难的.

充分利用 __import__

为了适应演示当前__import__API 的全部功能,这里有一个包装器函数,它具有更清晰,更好的文档API.

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)
Run Code Online (Sandbox Code Playgroud)

要证明,例如从姊妹包到巴兹:

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
Run Code Online (Sandbox Code Playgroud)

动态访问模块中的名称

要从baz模块按名称动态访问全局变量,请使用getattr.例如:

for name in dir(baz):
    print(getattr(baz, name))
Run Code Online (Sandbox Code Playgroud)

钩子改变导入行为

您可以使用__import__来更改或拦截导入行为.在这种情况下,让我们打印它所获得的参数来证明我们正在拦截它:

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
Run Code Online (Sandbox Code Playgroud)

现在,当您导入时,您可以看到这些重要的参数.

>>> from os.path import join as opj
name: 'os.path'
fromlist: ('join',)
level: 0
>>> opj
<function join at 0x7fd08d882618>
Run Code Online (Sandbox Code Playgroud)

也许在这种情况下获取全局变量或局部变量可能很有用,但是没有特定的用途可以立即想到.

  • 哇。这应该是公认的答案,并且还有更多的支持。谢谢。 (2认同)
  • 我不明白为什么这不是最好的答案。 (2认同)

Eri*_* H. 40

__import__函数将返回包的顶级模块,除非您传递非空fromlist参数:

_temp = __import__('foo.bar', fromlist=['object']) 
object = _temp.object
Run Code Online (Sandbox Code Playgroud)

请参阅有关该__import__函数的Python文档.

  • 或 `object = __import__('foo.bar.object', fromlist=[None])` 直接获取它。 (4认同)
  • 这个答案有3个问题:`level = -1`允许在Python 2中进行隐式相对导入 - 这被认为是错误的并从Python 3中删除,所以这个代码不再适用于Python 3.此外,`locals`什么都不做, `fromlist`只需要非空即可返回目标模块而不是root包.我在下面的答案中充分解释. (3认同)

Zul*_*ulu 22

您应该使用importlib.import_module,__import__不建议在口译员之外.

__import__的文档字符串:

导入模块.因为此函数适用于Python解释器而不是一般用途,所以最好使用importlib.import_module()以编程方式导入模块.

它还支持相对导入.

  • `__import__` 的文档指出:“模块的编程导入应该使用 import_module() 而不是这个函数。” 根据 https://docs.python.org/3/library/importlib.html (2认同)
  • 这超出了问题的范围。很明显他想使用/调查 dunder __import__。 (2认同)