2014-07-02 98 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