Python 动态更换部分功能代码
对于我提出的另一个问题,我提出了解决方案,即如何删除所有分散在函数代码中的调试输出函数的代价高昂的调用(使用空函数Python 动态更换部分功能代码,python,Python,对于我提出的另一个问题,我提出了解决方案,即如何删除所有分散在函数代码中的调试输出函数的代价高昂的调用(使用空函数lambda*p:None,速度降低了25倍) 解决方案是动态编辑函数代码,并在所有函数调用之前添加注释符号 我现在看到的问题是: #将所有命令行结束;但也可能有其他语句被隔开。这可以通过删除f中的所有pprint调用来解决,而不是进行注释,但它可能不是那么琐碎,因为可能存在嵌套的偏执 创建temp_f.py,然后从中加载新的f代码。应该有更好的方法来做到这一点,而不必写入硬盘驱动
lambda*p:None
,速度降低了25倍)
解决方案是动态编辑函数代码,并在所有函数调用之前添加注释符号
我现在看到的问题是:
将所有命令行结束;但也可能有其他语句被#
隔开代码>。这可以通过删除
中的所有f
调用来解决,而不是进行注释,但它可能不是那么琐碎,因为可能存在嵌套的偏执pprint
- 创建
,然后从中加载新的temp_f.py
代码。应该有更好的方法来做到这一点,而不必写入硬盘驱动器。我找到了,但没能成功f
- 如果应用decorator时使用了特殊语法
,则@debug
会在函数代码中包含带有decorator的行。这一行可以手动从字符串中删除,但如果有多个修饰符应用于inspect.getsource
,则可能会导致错误。我使用老式的decorator应用程序f
解决了这个问题f=decorator(f)
我认为在编译成字节码之前对函数代码进行预处理是一种非常有趣和有争议的技术。奇怪的是没有人对它感兴趣。我认为我给出的代码可能有很多不稳定的地方。装饰器可以返回包装器,也可以返回未经修改的装饰函数。使用它可以创建更好的调试器:
from functools import wraps
def debug(enabled=False):
if not enabled:
return lambda x: x # Noop, returns decorated function unaltered
def debug_decorator(f):
@wraps(f)
def print_start(*args, **kw):
print('{0}() started'.format(f.__name__))
try:
return f(*args, **kw)
finally:
print('{0}() completed'.format(f.__name__))
return print_start
return debug_decorator
debug
函数是一个decorator工厂,调用它时会生成一个decorator函数。如果调试被禁用,它只返回一个lambda,该lambda返回一个未更改的参数,即无操作修饰符。启用调试时,它返回一个调试修饰符,该修饰符在装饰函数启动时打印,并在其返回时再次打印
然后将返回的装饰器应用于装饰函数
用法:
DEBUG = True
@debug(DEBUG)
def my_function_to_be_tested():
print('Hello world!')
重申:当
DEBUG
设置为false时,my_function\u To_be_tested
保持不变,因此运行时性能根本不受影响。这是我在撰写了我在StackOverflow上提出的另一个问题的答案后提出的解决方案
此解决方案不注释任何内容,只删除独立的dprint
语句。它使用模块和抽象语法树,它让我们避免解析源代码。这个想法写在评论中
在必要的环境中,将写入temp_f.py
替换为执行f
。提出了这一解决方案
另外,最后一个解决方案解决了decorator递归应用程序的问题。它是通过使用\u blocked
全局变量来解决的
此代码解决问题中要求解决的问题。但仍然是:
你是对的,你不应该求助于这个,有这么多
它可能出错的方式。首先,Python不是一种专门为
源代码级转换,很难将其编写为转换器
例如注释_1,但不会无故破坏有效代码。第二
这种黑客可以在各种情况下破解-例如,
定义方法时,定义嵌套函数时,在中使用时
Cython,当inspect.getsource因任何原因失败时。Python是
足够动态,你真的不需要这种黑客来
自定义其行为
from\uuuuu future\uuuuu导入打印功能
调试=错误
def dprint(*args,**kwargs):
“调试打印”
打印(*args,**kwargs)
_阻塞=错误
def nodebug(name='dprint'):
''Decorator删除名为'name'为单独表达式的所有函数''
def助手(f):
全球封锁
如果被阻止:
返回f
导入检查,ast,系统
source=inspect.getsource(f)
a=ast.parse(source)#获取f的ast树
等级变压器(ast.NodeTransformer):
''将删除顶级''中包含'name'函数的所有表达式'
def visit_Expr(self,node):#访问所有表达式
尝试:
if node.value.func.id==name:#如果表达式由名为a的函数组成
返回无#删除它
除了(值错误):
通过
返回节点#返回节点未更改
transformer=transformer()
a_new=变压器。访问(a)
f_new_compiled=compile(a_new,,'exec')
env=系统模块[f.\uuuuu模块\uuuuuuuu]。\uuuuuu指令__
_阻塞=真
尝试:
执行官(新编辑,环境)
最后:
_阻塞=错误
返回环境[f.\U\U名称\U]
返回助手
@nodebug('dprint')
def():
dprint('f()已启动')
打印(“重要输出”)
dprint('f()结束')
打印(“重要输出2”)
f()
其他相关链接:
如果未启用:在开始时返回f
。@MartijnPieters Martijn,我不明白你的意思!:)else:return f
不一样吗?快速浏览一下,没有发现有人提到过它,但可能我遗漏了什么:我很确定print
函数之所以如此缓慢,即使是在lambda*p:None
形式中,是因为无论您设置了什么print
DEBUG = True
@debug(DEBUG)
def my_function_to_be_tested():
print('Hello world!')
from __future__ import print_function
DEBUG = False
def dprint(*args,**kwargs):
'''Debug print'''
print(*args,**kwargs)
_blocked = False
def nodebug(name='dprint'):
'''Decorator to remove all functions with name 'name' being a separate expressions'''
def helper(f):
global _blocked
if _blocked:
return f
import inspect, ast, sys
source = inspect.getsource(f)
a = ast.parse(source) #get ast tree of f
class Transformer(ast.NodeTransformer):
'''Will delete all expressions containing 'name' functions at the top level'''
def visit_Expr(self, node): #visit all expressions
try:
if node.value.func.id == name: #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__]
return helper
@nodebug('dprint')
def f():
dprint('f() started')
print('Important output')
dprint('f() ended')
print('Important output2')
f()