我曾尝试为Python 3开发一个“模块扩展器”工具,但存在一些问题。
想法如下:对于给定的Python脚本main.py
,该工具expanded_main.py
通过将每个import语句替换为导入模块的实际代码来生成功能上等效的Python脚本;这假设导入的Python源代码是可访问的。为了正确地完成这项工作,我使用了ast
Python的内置模块以及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"
),但greetings函数引发异常:NameError: name 'g' is not defined
。实际上发生的是
my_module2
,g
是一个全局变量,my_module2
,g
是一个类变量,应称为my_module2.g
。当您在中定义函数,类等时,my_module2.py
并希望在相同的其他函数,类中引用它们时,也会发生其他类似的副作用my_module2.py
。
知道如何解决这些问题吗?
除了类之外,还有其他的Python结构可以模仿模块吗?
最后说明:我知道该工具应注意嵌套导入(递归)的1°,同一模块可能多次导入的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()
通过使用实例替换它们,可以自然地处理嵌套导入Importer
。重复导入也不是问题。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句