如何在不导入的情况下模仿Python模块?

时间:2017-07-11 14:04:21

标签: python import

我尝试为Python 3开发一个“模块扩展器”工具,但我遇到了一些问题。

这个想法如下:对于给定的Python脚本main.py,该工具通过将每个import语句替换为导入模块的实际代码来生成功能等效的Python脚本expanded_main.py;这假设导入的Python源代码是可访问的。为了正确地完成工作,我正在使用Python的内置模块ast以及astor,这是一个允许将AST转储回Python源的第三方工具。这个导入扩展器的动机是能够将脚本编译成一个单字节码块,因此Python VM不应该负责导入模块(例如,这可能对MicroPython很有用)。

最简单的情况是声明:

from import my_module1 import *

要转换它,我的工具会查找文件my_module1.py,并用该文件的内容替换import语句。然后,expanded_main.py可以访问my_module中定义的任何名称,就好像模块是以正常方式导入的一样。我不关心可能揭示诀窍的微妙副作用。另外,为了简化,我将from import my_module1 import a, b, c视为先前的导入(带星号),而不关心可能的副作用。到目前为止一切都很好。

现在这是我的观点。你怎么能处理这种导入的风格:

import my_module2

我的第一个想法是通过创建一个与模块同名的类并复制缩进的Python文件内容来模仿这个:

class my_module2:
    # content of my_module2.py
    …

这实际上适用于许多情况但是,遗憾的是,我发现这有几个小故障:其中之一是它失败了,函数的主体引用了模块中定义的全局变量。例如,请考虑以下两个Python文件:

# my_module2.py 
g = "Hello"
def greetings():
    print (g + " World!")

# main.py
import my_module2
print(my_module2.g)
my_module2.greetings()

执行时,main.py会打印"Hello""Hello World!"。现在,我的扩展器工具将产生这个:

# expanded_main.py
class my_module2:
    g = "Hello"
    def greetings():
        print (g + " World!")
print(my_module2.g)
my_module2.greetings()

在执行expanded_main.py时,第一个打印语句是OK("Hello"),但问候语函数引发异常:NameError: name 'g' is not defined。 实际发生的是

    模块中
  • my_module2g是一个全局变量,
  • my_module2中,g是一个类变量,应该称为my_module2.g

当您在my_module2.py中定义函数,类,...时,您希望在同一my_module2.py的其他函数,类......中引用它们时,会发生其他类似的副作用。

知道如何解决这些问题吗?

除了类之外,还有其他允许模仿模块的Python构造吗?

最后说明:我知道该工具应该注意1°的嵌套导入(递归),2°可能多次导入同一个模块。我不希望在这里讨论这些主题。

1 个答案:

答案 0 :(得分:1)

您可以在 function 的范围内执行模块的源代码,特别是实例方法。然后,可以通过在相应类上定义__getattr__并保留初始函数locals()的副本来使属性可用。以下是一些示例代码:

class Importer:
    def __init__(self):

        g = "Hello"

        def greetings():
            print (g + " World!")

        self._attributes = locals()

    def __getattr__(self, item):
        return self._attributes[item]


module1 = Importer()
print(module1.g)
module1.greetings()

通过使用Importer的实例以相同的方式替换它们,可以自然地处理嵌套导入。重复导入也不是问题。