我尝试为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_module2
,g
是一个全局变量,
my_module2
中,g
是一个类变量,应该称为my_module2.g
。当您在my_module2.py
中定义函数,类,...时,您希望在同一my_module2.py
的其他函数,类......中引用它们时,会发生其他类似的副作用。
知道如何解决这些问题吗?
除了类之外,还有其他允许模仿模块的Python构造吗?
最后说明:我知道该工具应该注意1°的嵌套导入(递归),2°可能多次导入同一个模块。我不希望在这里讨论这些主题。
答案 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
的实例以相同的方式替换它们,可以自然地处理嵌套导入。重复导入也不是问题。