是否可以从 python 中的模块对象生成导入语句?

Is it possible to generate import statement from the module object in python?

在Python中,用isinstance(obj, types.ModuleType)可以很容易地检查一个对象是否是一个模块。我们还可以以编程方式生成模块。然而,我有兴趣采用另一种方式 - 生成的代码会创建一个导入,导致模块被添加到 globals/locals 命名空间。基本上是一个像这样的函数 assemble_import

def assemble_import(module: types.ModuleType, name: str) -> str:
    pass

大致满足以下条件:

import statistics
assert assemble_import(statistics, 'statistics') = 'import statistics'

from os import path
assert assemble_import(path, 'path') = 'from os import path'

import collections.abc
abc = collections.abc
assert assemble_import(abc, 'abc') = 'from collections import abc'

import abc as xyz
assert assemble_import(xyz, 'xyz') = 'import abc as xyz'

希望它使用抽象语法树,而是模块对象本身。到目前为止我尝试过的:

name 属性的问题是 'posixpath' 对应 os.path,而 from os import posixpath 显然不起作用。另外,我看不到如何获取父模块(os.path 示例中的 os)。

是否可以在 Python 中实现可行的(虽然不一定是 production-ready/bulletproof)解决方案?换句话说,是否需要有关包结构的信息来重新创建导入代码preserved/accessible?

可以,而且很简单,但是os.path会有点奇怪:

def assemble_import(module, name):
    return 'import {} as {}'.format(module.__name__, name)

os.path 很奇怪,因为它是 ntpath 模块或 posixpath 模块的依赖于平台的别名。此函数将使用模块的实际名称,ntpathposixpath。如果您想将 os.path 视为 os.path,您可以对其进行特殊处理,尽管它可能不是最佳设计选择。

对于 actual 包子模块,如 collections.abc,此函数会将它们视为包含包的子模块:

>>> assemble_import(collections.abc, 'abc')
'import collections.abc as abc'

但对于 os.path,这将为您提供类似

的输出
>>> assemble_import(os.path, 'path')
'import posixpath as path'

如果您希望导入看起来更像人类通常编写的内容,您可以向函数添加一些逻辑:

def assemble_import(module, name):
    pname, _, mname = module.__name__.rpartition('.')
    if pname:
       statement = 'from {} import {}'.format(pname, mname)
    else:
       statement = 'import ' + mname
    if mname != name:
       statement += ' as ' + name
    return statement