如何使用“导入”功能从子模块导入名称?

2024-05-07 00:45:00 发布

您现在位置:Python中文网/ 问答频道 /正文

我试图使用__import__函数复制from foo.bar import object,但我似乎遇到了问题。

一个简单的例子from glob import glob很简单:glob = __import__("glob").glob

我遇到的问题是,我正在从子包导入一个名称(即from foo.bar):

所以我想要的是

string_to_import = "bar"
object = __import__("foo." + string_to_import).object

但这只是导入了顶级的foo包,而不是foo.bar子包:

__import__("foo.bar")
<module 'foo' from 'foo/__init__.pyc'>

Tags: to函数fromimport名称stringobjectfoo
3条回答

How to use python's __import__() function properly?

有两种用途:

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

大部分情况下,你也不需要这么做。

用于用户空间导入

最佳实践是改用importlib。但如果你坚持:

一般用法:

>>> sys = __import__('sys')
>>> sys
<module 'sys' (built-in)>

复杂:

>>> 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'>

如果需要名称中最右边的子模块,请将非空列表(例如[None])传递给fromlist

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

或者,如文档所述,使用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'>

文件

__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.

如果您仔细阅读它,就会感觉到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)

要演示,例如从姐妹套餐到baz:

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

动态访问模块中的名称

要按名称从baz模块动态访问globals,请使用getattr。例如:

for name in dir(baz):
    print(getattr(baz, name))

挂钩以更改导入行为

您可以使用__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

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

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

在这种情况下,获取全局或局部可能会很有用,但不会立即想到这方面的具体用途。

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

_temp = __import__('foo.bar', fromlist=['object']) 
object = _temp.object

请参阅^{} function上的Python文档。

您应该使用importlib.import_module,不建议在解释器外部使用__import__

__import__的docstring中:

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.

它还支持relative imports

相关问题 更多 >