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

如何在不导入的情况下模拟Python模块?,python,import,Python,Import,我曾尝试为Python 3开发一个«模块扩展器»工具,但我遇到了一些问题 其思想如下:对于给定的Python脚本main.py,该工具通过用导入模块的实际代码替换每个导入语句,生成功能等效的Python脚本expanded_main.py;这假设导入的Python源代码是可访问的。为了正确地完成这项工作,我使用了Python的内置模块ast,以及astor,这是一个允许将ast转储回Python源代码的第三方工具。这个导入扩展器的动机是能够将脚本编译成单个字节码块,因此Python VM不应该负

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

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

最简单的情况是:

from import my_module1 import *
要对此进行转换,我的工具将查找一个文件
my_module1.py
,并用该文件的内容替换导入语句。然后,
expanded_main.py
可以访问
my_module
中定义的任何名称,就好像模块是以正常方式导入的一样。我不在乎那些可能会泄露秘密的微妙的副作用。此外,为了简化,我将导入模块1导入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
打印
“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:未定义名称“g”
。 实际上发生的是

  • 在模块
    my_module2
    中,
    g
    是一个全局变量
  • 在类
    my_module2
    中,
    g
    是一个类变量,它应该被称为
    my_module2.g
当您在
my_module2.py
中定义函数、类等,并希望在相同
my_module2.py
的其他函数、类等中引用它们时,会发生其他类似的副作用

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

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


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

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

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
的实例以相同的方式替换嵌套导入,可以自然地处理嵌套导入。重复导入也不应该是问题。

@Ignacio:正如我在消息中所说的,这是Python 3;我已经在3.4和3.6中测试过了。我知道在Python2中,异常是不同的(“TypeError:unbound method greetings()必须以我的_module2实例作为第一个参数调用(而不是什么都没有)”,至少在Python2.6中是这样);目标是拥有一个自包含的脚本,该脚本本身可以编译成一个自包含的Python字节码文件。执行此操作不需要在运行时依赖Python importi逻辑。也不会访问文件系统。这对于资源受限的系统(例如微控制器上的MicroPython)可能很有用。如果您将库“出售”到应用程序本地的文件夹中(即不在
网站包中
),您将获得90%的好处,并且不必围绕这个问题进行编码。您是否构建了这样一个工具(将外部代码嵌入到主脚本中) ? 也许您已经找到了另一个执行相同任务的工具?你愿意分享吗?谢谢!这很聪明,这正是我想要的。
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()