2014-07-02 62 views
4

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

例如,下面我尝试在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 

在这种新情况下,打印每个“条”之前,我想打印一个“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 

回答

6

也许你正在寻找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,您可以访问foo的当地人和全球使用frame.f_localsframe.f_globals

有关sys.settrace如何用于调试的示例,请参阅此post


如何从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() 
+0

非常感谢,这似乎是接近于此,你说得对,线程的主要目的不是调试,而是更多的是能够阻止正在执行的代码块在执行代码的每一行之前更改此函数将评估的另一个全局变量的值。 如果全局切换为false,则这将在代码的每一行内添加一个抢占点,然后如果切换为true,那么函数停止withtout到结束=>然后该函数执行该行并转到下一个一个 我是否明确了这一点? – afiah

+0

是的,您可以检查*并从'monitor'内更改本地和全局值的值。 – unutbu

+0

谢谢,但是我可以停止监视功能的当前执行吗?在这种情况下,停止执行foo()? (在某种程度上,我可以在我想要的时候添加返回行以完成我的功能?) – afiah

0

这听起来像你需要一个调试器,看看内建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,它向前迈进了一步。

0

最好的答案很可能取决于你真正想做的事,但为了你问什么,我应该这样做:

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"))))) 
+0

这不符合我正在寻找,因为它确实改变了富路()函数创建编码兰巴3的功能,我在寻找一种方法来保持编码的经典方式,并添加装饰将执行所有这些工作。 – afiah

+0

顺便说一句:有一些行为无法轻松处理,想象一个带有for循环的foo()函数,我想在循环的每个循环中执行我的print'hello',在某种程度上,我正在搜索抢占点在代码的每一行 – afiah