我决定尝试在将函数文本编译成字节代码并执行之后对其进行预处理。这仅用于培训。我很难想象它会成为一个令人满意的解决方案。我遇到过一个我想以这种方式解决的问题,但最终找到了一个更好的方法。所以这只是为了培训和学习新东西,而不是真正的用途。
假设我们有一个函数,在编译之前我们想要修改哪些源代码:
def f():
1;a()
print('Some statements 1')
1;a()
print('Some statements 2')
例如,让我们用1;
标记它的某些行,因为它们有时会被评论,有时则不会。我只是以它为例,功能的修改可能会有所不同。
为了评论这些线条,我做了一个装饰师。它的全部代码如下:
from __future__ import print_function
def a():
print('a()')
def comment_1(s):
lines = s.split('\n')
return '\n'.join(line.replace(';','#;',1) if line.strip().startswith('1;') else line for line in lines)
def remove_1(f):
import inspect
source = inspect.getsource(f)
new_source = comment_1(source)
with open('temp.py','w') as file:
file.write(new_source)
from temp import f as f_new
return f_new
def f():
1;a()
print('Some statements 1')
1;a()
print('Some statements 2')
f = remove_1(f) #If decorator @remove is used above f(), inspect.getsource includes @remove inside the code.
f()
我使用inspect.getsourcelines来检索函数f
代码。然后我做了一些文本处理(在这种情况下注释以1;
开头的行)。之后,我将其保存到temp.py
模块,然后导入。然后在主模块中修饰函数f
。
应用装饰器时的输出是:
Some statements 1
Some statements 2
未申请时是:
a()
Some statements 1
a()
Some statements 2
我不喜欢的是我必须使用硬盘来加载编译功能。可以在不将其写入临时模块temp.py
并从中导入的情况下完成吗?
第二个问题是将装饰器放在f
上方@replace
。当我这样做时,inspect.getsourcelines
会使用此装饰器返回f
文本。我可以手动从f
的文本中删除。但这样做会非常危险,因为可能会应用多个装饰器。所以我采用了旧式的装饰语法f = remove_1(f)
来完成这项工作。但是,是否可以使用@replace
?
答案 0 :(得分:1)
可以通过在源上调用exec
语句来避免创建临时文件。 (如果您想要对编译进行额外控制,也可以在compile
之前显式调用exec
,但是exec
将为您进行编译,因此没有必要。)正确调用{{1}如果它从模块的命名空间访问全局变量,那么函数将正常工作的额外好处。
第二个问题中描述的问题可以通过暂时阻止装饰器运行来解决。就这样,装饰者和其他所有装饰者一样,但是没有操作。
这是更新的来源。
exec
答案 1 :(得分:0)
我将留下 user4815162342 在答案中给出的解决方案的修改版本。它使用ast
模块删除f
的某些部分,如comment to the question中所述。为了实现这一点,我主要依赖this article中的信息。
此实现删除所有出现的a
作为独立表达式。
from __future__ import print_function
import sys
import ast
import inspect
def a():
print('a() is called')
_blocked = False
def remove_1(f):
global _blocked
if _blocked:
return f
import inspect
source = inspect.getsource(f)
a = ast.parse(source) #get ast tree of f
class Transformer(ast.NodeTransformer):
'''Will delete all expressions containing 'a' functions at the top level'''
def visit_Expr(self, node): #visit all expressions
try:
if node.value.func.id == 'a': #if expression consists of function with name a
return None #delete it
except(ValueError):
pass
return node #return node unchanged
transformer = Transformer()
a_new = transformer.visit(a)
f_new_compiled = compile(a_new,'<string>','exec')
env = sys.modules[f.__module__].__dict__
_blocked = True
try:
exec(f_new_compiled,env)
finally:
_blocked = False
return env[f.__name__]
@remove_1
def f():
a();a()
print('Some statements 1')
a()
print('Some statements 2')
f()
输出结果为:
Some statements 1
Some statements 2