2016-12-15 53 views
-1

我已閱讀this on SO等問題,我相信我明白了使用__getattribute__的危險。但是,最近我接管了其他人的項目,我需要做一些修改。我認爲理解一個項目的最好方法是追蹤它 - 所以我插入了pdb.set_trace(),然後按「n \ r \ n」 - 但該程序沒有被執行到下一行並等待新的輸入,而是繼續執行到最後。搜索後,我認爲這是造成這個問題的錯誤使用__getattribute__,但我不知道爲什麼。我簡化代碼爲以下:以Python代碼片段爲例,瞭解__getattribute__和pdb.set_trace()作爲示例

class TestAttribute(object): 
    """docstring for TestAttribute""" 
    def __init__(self, is_testing=False): 
     super(TestAttribute, self).__init__() 
     self.is_testing = is_testing 

    def __getattribute__(self, name): 
     # print(name) 
     try: 
      # the line below will trigger the recursion error 
      if self.is_testing: 
       name = name.upper() 
      return super(TestAttribute, self).__getattribute__(name) 
     except AttributeError: 
      return None 
     except Exception: 
      # this line is added by me to see the output 
      import traceback; traceback.print_exc(); 
      return None 

    def __getitem__(self, name): 
     return self.__getattribute__(name) 

    def __setitem__(self, name, val): 
     return self.__setattr__(name, val) 

    def __setattr__(self, name, val): 
     # so this func will be called in __init__ and will 
     # enter __getattribute__ 
     if self.is_testing: 
      name = name.lower() 
     super(TestAttribute, self).__setattr__(name, val) 


if __name__ == '__main__': 
    ttt = TestAttribute() 
    import pdb; pdb.set_trace() 
    ttt.k = 1 
    print('test done') 
    print('test done again') 
    print('test done again') 
    print('test done again') 

輸出如下:

Traceback (most recent call last): 
    File "test_getattribute.py", line 10, in __getattribute__ 
Traceback (most recent call last): 
    File "test_getattribute.py", line 10, in __getattribute__ 
    if self.is_testing: 
    File "test_getattribute.py", line 16, in __getattribute__ 
    import traceback; traceback.print_exc(); 
    File "/usr/lib/python2.7/traceback.py", line 232, in print_exc 
    print_exception(etype, value, tb, limit, file) 
    File "/usr/lib/python2.7/traceback.py", line 125, in print_exception 
    print_tb(tb, limit, file) 
    File "/usr/lib/python2.7/traceback.py", line 69, in print_tb 
    line = linecache.getline(filename, lineno, f.f_globals) 
    File "/home/jgu/repos/.venv/lib/python2.7/linecache.py", line 14, in getline 
    lines = getlines(filename, module_globals) 
    File "/home/jgu/repos/.venv/lib/python2.7/linecache.py", line 40, in getlines 
    return updatecache(filename, module_globals) 
RuntimeError: maximum recursion depth exceeded 
> /home/jgu/repos/dat_cs/test_getattribute.py(34)<module>() 
-> ttt.k = 1 
(Pdb) n 
Traceback (most recent call last): 
    File "test_getattribute.py", line 10, in __getattribute__ 
    if self.is_testing: 
    File "test_getattribute.py", line 7, in __getattribute__ 
    def __getattribute__(self, name): 
    File "/usr/lib/python2.7/bdb.py", line 50, in trace_dispatch 
    return self.dispatch_call(frame, arg) 
    File "/usr/lib/python2.7/bdb.py", line 76, in dispatch_call 
    if not (self.stop_here(frame) or self.break_anywhere(frame)): 
    File "/usr/lib/python2.7/bdb.py", line 147, in break_anywhere 
    return self.canonic(frame.f_code.co_filename) in self.breaks 
    File "/usr/lib/python2.7/bdb.py", line 29, in canonic 
    if filename == "<" + filename[1:-1] + ">": 
RuntimeError: maximum recursion depth exceeded in cmp 
test done 
test done again 
test done again 
test done again 

正如你所看到的,我只能按「N \ r \ n」,將繼續執行一路程序飾面。

而且還有另一個小問題,如果我沒有PDB跑,我看到這樣的輸出:

Traceback (most recent call last): 
    File "test_getattribute.py", line 10, in __getattribute__ 
Traceback (most recent call last): 
    File "test_getattribute.py", line 10, in __getattribute__ 
    if self.is_testing: 
    File "test_getattribute.py", line 16, in __getattribute__ 
    import traceback; traceback.print_exc(); 
    File "/usr/lib/python2.7/traceback.py", line 232, in print_exc 
    print_exception(etype, value, tb, limit, file) 
    File "/usr/lib/python2.7/traceback.py", line 125, in print_exception 
    print_tb(tb, limit, file) 
    File "/usr/lib/python2.7/traceback.py", line 69, in print_tb 
    line = linecache.getline(filename, lineno, f.f_globals) 
    File "/home/jgu/repos/.venv/lib/python2.7/linecache.py", line 14, in getline 
    lines = getlines(filename, module_globals) 
    File "/home/jgu/repos/.venv/lib/python2.7/linecache.py", line 40, in getlines 
    return updatecache(filename, module_globals) 
RuntimeError: maximum recursion depth exceeded 
Traceback (most recent call last): 
Traceback (most recent call last): 
    File "test_getattribute.py", line 10, in __getattribute__ 
    if self.is_testing: 
    File "test_getattribute.py", line 16, in __getattribute__ 
    import traceback; traceback.print_exc(); 
    File "/usr/lib/python2.7/traceback.py", line 232, in print_exc 
    print_exception(etype, value, tb, limit, file) 
    File "/usr/lib/python2.7/traceback.py", line 125, in print_exception 
    print_tb(tb, limit, file) 
    File "/usr/lib/python2.7/traceback.py", line 67, in print_tb 
    ' File "%s", line %d, in %s' % (filename, lineno, name)) 
RuntimeError: <unprintable RuntimeError object> 
test done 
test done again 
test done again 
test done again 

所以打印不正確的第二個錯誤,這是爲什麼?

編輯:我不問爲什麼會出現遞歸錯誤。我相信我很清楚 - 所以請先理解我的問題。謝謝

+0

'if self.is_testing' - 那麼,你如何找到'self.is_testing'的值?你可以調用'__getattribute__'。要執行'__getattribute__',你要做的第一件事就是檢查'self.is_testing'。你如何找到'self.is_testing'的價值?你可以調用'__getattribute__' ... – user2357112

+0

我不問爲什麼會出現遞歸錯誤。我想我很清楚這一點。 –

回答

2

pdb使用sys.settrace設置跟蹤功能來完成其工作。任何傳播出trace函數的異常都會禁用它。 RuntimeError發生在trace函數內部,一旦發生,實質上會關閉pdb。

+0

明白了。感謝您的解釋。第二個問題呢?只是想知道你是否可以提供幫助 –

+0

@JunchaoGu:當你捕捉到RuntimeError時,堆棧仍然很瘋狂,並且當你嘗試顯示堆棧跟蹤時,在另一個堆棧溢出的位置觸發一個[某些回退](https://hg.python.org/cpython/file/2.7/Lib/traceback.py#l212)。 – user2357112

+0

順便說一句,你真的不應該在'__getattribute__'中捕獲這些異常。 – user2357112