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
    ,则
    inspect.getsource
    会在函数代码中包含带有decorator的行。这一行可以手动从字符串中删除,但如果有多个修饰符应用于
    f
    ,则可能会导致错误。我使用老式的decorator应用程序
    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()
其他相关链接:

哦,哇,这听起来很乏味。Python需要一个预编译器。使用一个合适的装饰器,当调试被禁用时,该装饰器会返回未更改的装饰函数吗
如果未启用:在开始时返回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()