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

Pie*_*nis 5 python import

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

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

最简单的情况是以下语句:

from import my_module1 import *
Run Code Online (Sandbox Code Playgroud)

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

现在这是我的观点。你怎么能处理这种进口的味道:

import my_module2
Run Code Online (Sandbox Code Playgroud)

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

class my_module2:
    # content of my_module2.py
    …
Run Code Online (Sandbox Code Playgroud)

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

# my_module2.py 
g = "Hello"
def greetings():
    print (g + " World!")
Run Code Online (Sandbox Code Playgroud)

和

# main.py
import my_module2
print(my_module2.g)
my_module2.greetings()
Run Code Online (Sandbox Code Playgroud)

在执行时,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()
Run Code Online (Sandbox Code Playgroud)

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

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

当你定义功能,...在课堂上其他类似的副作用发生my_module2.py,你想指他们在其他函数,类...相同的my_module2.py。

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

除了类,是否还有其他允许模仿模块的 Python 构造?

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

a_g*_*est 2

您可以在函数(特别是实例方法)的范围内执行模块的源代码。然后,可以通过在相应的类上进行定义__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()
Run Code Online (Sandbox Code Playgroud)

嵌套导入可以通过以相同方式用 的实例替换来自然地处理Importer。重复导入也不应该成为问题。