【问题标题】:Python : Adding a code routine at each line of a block of codePython:在代码块的每一行添加一个代码例程
【发布时间】:2014-08-23 05:26:50
【问题描述】:

我希望在另一块代码的每一行之后运行一段代码。例如,希望能够在执行函数的下一行之前或之后评估全局变量。

例如,下面我尝试在foo() 函数的每一行之前打印'hello'。我认为装饰器可以帮助我,但它需要一些自省功能才能编辑我的 foo() 函数的每一行并在它之前或之后添加我想要的内容。

我正在尝试执行类似的操作:

>>> def foo():
...    print 'bar'
...    print 'barbar'
...    print 'barbarbar'

>>> foo()
hello
bar
hello
barbar 
hello
barbarbar

我该如何执行此操作? __code__ 对象会有帮助吗?我需要一个装饰器和内省吗?

编辑:这是该线程目标的另一个示例:

>>> def foo():
...    for i in range(0,3):
...        print 'bar'

>>> foo()
hello
bar
hello
bar 
hello
bar

在这个新案例中,在打印每个“bar”之前,我想打印一个“hello”。

这样做的主要目标是能够在执行下一行代码之前执行另一个函数或测试任何类型的全局变量。想象一下,如果一个全局变量是True,那么代码转到下一行;而如果全局变量为False,则停止函数执行。

编辑: 在某种程度上,我正在寻找一种将代码注入另一个代码块的工具。

编辑: 感谢 unutbu 我已经实现了这个代码:

import sys
import time
import threading

class SetTrace(object):
    """
    with SetTrace(monitor):
    """
    def __init__(self, func):
        self.func = func
    def __enter__(self):
        sys.settrace(self.func)
        return self
    def __exit__(self, ext_type, exc_value, traceback):
        sys.settrace(None)
        # http://effbot.org/zone/python-with-statement.htm
        # When __exit__ returns True, the exception is swallowed.
        # When __exit__ returns False, the exception is reraised.
        # This catches Sentinel, and lets other errors through
        # return isinstance(exc_value, Exception)

def monitor(frame, event, arg):
    if event == "line":
        if not running:
            raise Exception("global running is False, exiting")
    return monitor

def isRunning(function):
    def defaultBehavior(*args):
        with SetTrace(monitor):
            ret = function(*args)
            return ret
    return defaultBehavior

@isRunning
def foo():
    while True:
        time.sleep(1)
        print 'bar'

global running
running = True
thread = threading.Thread(target = foo)
thread.start()
time.sleep(3)
running = False

【问题讨论】:

    标签: python decorator introspection


    【解决方案1】:

    也许你正在寻找sys.settrace

    import sys
    class SetTrace(object):
        def __init__(self, func):
            self.func = func
    
        def __enter__(self):
            sys.settrace(self.func)
            return self
    
        def __exit__(self, ext_type, exc_value, traceback):
            sys.settrace(None)
    
    def monitor(frame, event, arg):
        if event == "line":
            print('hello')
            # print(frame.f_globals) 
            # print(frame.f_locals)  
        return monitor
    
    
    
    def foo():
       print 'bar'
       print 'barbar'
       print 'barbarbar'
    
    with SetTrace(monitor):
        foo()
    

    产量

    hello
    bar
    hello
    barbar
    hello
    barbarbar
    hello
    

    monitor 中,您可以使用frame.f_localsframe.f_globals 访问foo 的局部变量和全局变量。

    请参阅此post,了解如何使用sys.settrace 进行调试。


    如何在monitor 内阻止foo

    最优雅的方法是在foo 中放置一个条件语句,以便foo 检查何时退出。然后,您可以在monitor 中操纵条件的值来控制foo 何时退出。

    但是,如果您不想或不能更改foo,则另一种方法是从monitor 中引发异常。异常将通过帧堆栈冒泡,直到被捕获。如果您在SetTrace.__exit__ 中捕获它,那么控制流将继续进行,就像foo 刚刚退出一样。

    import sys
    class Sentinel(Exception): pass
    
    class SetTrace(object):
        """
        with SetTrace(monitor):
            ...
        """
        def __init__(self, func):
            self.func = func
    
        def __enter__(self):
            sys.settrace(self.func)
            return self
    
        def __exit__(self, ext_type, exc_value, traceback):
            sys.settrace(None)
            # http://effbot.org/zone/python-with-statement.htm
            # When __exit__ returns True, the exception is swallowed.
            # When __exit__ returns False, the exception is reraised.
    
            # This catches Sentinel, and lets other errors through
            return isinstance(exc_value, Sentinel)
    
    def monitor(frame, event, arg):
        if event == "line":
            l = frame.f_locals
            x = l.get('x', 0)
            print('x = {}'.format(x))
            if x > 3:
                raise Sentinel()
        return monitor
    
    def foo():
        x = 0
        while True:
            print 'bar'
            x += 1
    
    with SetTrace(monitor):
        foo()
    

    【讨论】:

    • 非常感谢,这似乎接近你是对的,线程的重点不是调试,而是能够通过更改来停止正在执行的代码块在执行每一行代码之前,此函数将评估的另一个全局变量的值。这将在代码的每一行内添加一个抢占点,如果全局切换为 false =>,则函数停止而不结束,如果切换为 true =>,则函数执行该行并转到下一个 Do我说得够清楚了吗?
    • 是的,您可以在monitor 中检查并更改局部变量和全局变量的值。
    • 谢谢,但是我可以停止当前执行被监控的函数吗?在那种情况下,阻止 foo() 被执行? (在某种程度上,我可以在我想要的时候添加返回线以完成我的功能吗?)
    • 我编辑了上面的帖子,展示了如何从monitor 中终止foo
    • 非常感谢,它确实有效,我会在我的帖子中添加个人修改
    【解决方案2】:

    最好的答案可能取决于你真正想要做什么,但为了做到你所要求的,我会这样做:

    from itertools import chain
    
    def print_(s):
        print s
    
    def print_f(s):
       return (lambda: print_(s))
    
    def execute_several(functions):
        for f in functions:
            f()
    
    def prepend_printhello(function):
        return (print_f("hello"), function)
    
    def foo():
        execute_several(chain(*map(prepend_printhello, map(print_f, ("bar", "barbar", "barbarbar")))))
    

    【讨论】:

    • 这与我正在搜索的内容不对应,因为它确实改变了 foo() 函数的编码方式,创建了 3 个 Lamba 函数,我正在寻找一种方法来保持经典的编码方式并添加将执行所有这些工作的装饰器。
    • 顺便说一句:有些行为不容易处理,想象一个带有 for 循环的 foo() 函数,我想在这个循环的每个循环中执行我的 print 'hello',在一种方式,我在代码的每一行寻找抢占点
    【解决方案3】:

    听起来你需要一个调试器,看看内置的pdb。使用 pdb 你可以这样做:

    >>> def foo():
    ...     import pdb;pdb.set_trace()
    ...     print 'bar'
    ...     print 'barbar'
    ...     print 'barbarbar'
    ... 
    >>> 
    >>> foo()
    > <stdin>(3)foo()
    (Pdb) print 'hi'
    hi
    (Pdb) n
    bar
    > <stdin>(4)foo()
    (Pdb) n
    barbar
    > <stdin>(5)foo()
    (Pdb) n
    barbarbar
    --Return--
    > <stdin>(5)foo()->None
    (Pdb) n
    --Return--
    > <stdin>(1)<module>()->None
    (Pdb) n
    >>> 
    

    与大多数其他调试器一样,这允许您逐行执行代码。您可以查看文档以获取更多信息,但在上面的示例中,pdb.set_trace() -call 设置了调试入口点并打开了 pdb 控制台。您可以从控制台修改变量并执行各种操作。 n 只是 next 的简写,它向前迈了一步。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-11-18
      • 2011-04-17
      • 1970-01-01
      • 1970-01-01
      • 2013-09-03
      • 1970-01-01
      • 2012-08-08
      相关资源
      最近更新 更多