14 python異常處理,調試,單元測試以及文檔測試

1 python錯誤處理

1 try的運行機制

利用打印錯誤這種方式很low

def bar():
    r = foo()
    if r==(-1):
        print('Error')
    else:
        pass

所以高級語言通常都內置了一套try…except…finally…的錯誤處理機制,Python也不例外。

try:
    print('try...')
    r = 10 / 0
    print('result:', r)#這裏try後出現錯誤,則後續代碼不會繼續執行
except ZeroDivisionError as e:#如果有這個錯誤則跳到這裏進行處理,而不是處理後續代碼
    print('except:', e)
finally:#如果有finally語句塊,則except後會執行
    print('finally...')
print('END')
try...
('except:', ZeroDivisionError('integer division or modulo by zero',))
finally...
END

總結如下,try如果遇到錯誤不往下執行代碼,而是跳到except執行,然後再inally,再到最後跳過了錯誤?

沒有錯誤發生,所以except語句塊不會被執行,但是finally如果有,則一定會被執行(可以沒有finally語句)。

你還可以猜測,錯誤應該有很多種類,如果發生了不同類型的錯誤,應該由不同的except語句塊處理。沒錯,可以有多個except來捕獲不同類型的錯誤:

2 多類異常

try:
    print('try...')
    r = 10 / int('a')#int也可以拋出一個異常,判斷這個數是否爲整數
    print('result:', r)
except ValueError as e:
    print('ValueError:', e)
except ZeroDivisionError as e:
    print('ZeroDivisionError:', e)
finally:
    print('finally...')
print('END')
try...
('ValueError:', ValueError("invalid literal for int() with base 10: 'a'",))
finally...
END
#多類異常注意事項
try:
    print str(1)
except ValueError as e:
    print('ValueError')
except UnicodeError as e:
    print('UnicodeError')
1

第二個except永遠也捕獲不到UnicodeError,因爲UnicodeError是ValueError的子類,如果有,也被第一個except給捕獲了。

常見異常類型及繼承關係如下:https://docs.python.org/3/library/exceptions.html#exception-hierarchy

BaseException
 +-- SystemExit
 +-- KeyboardInterrupt
 +-- GeneratorExit
 +-- Exception
      +-- StopIteration
      +-- StopAsyncIteration
      +-- ArithmeticError
      |    +-- FloatingPointError
      |    +-- OverflowError
      |    +-- ZeroDivisionError
      +-- AssertionError
      +-- AttributeError
      +-- BufferError
      +-- EOFError
      +-- ImportError
           +-- ModuleNotFoundError
      +-- LookupError
      |    +-- IndexError
      |    +-- KeyError
      +-- MemoryError
      +-- NameError
      |    +-- UnboundLocalError
      +-- OSError
      |    +-- BlockingIOError
      |    +-- ChildProcessError
      |    +-- ConnectionError
      |    |    +-- BrokenPipeError
      |    |    +-- ConnectionAbortedError
      |    |    +-- ConnectionRefusedError
      |    |    +-- ConnectionResetError
      |    +-- FileExistsError
      |    +-- FileNotFoundError
      |    +-- InterruptedError
      |    +-- IsADirectoryError
      |    +-- NotADirectoryError
      |    +-- PermissionError
      |    +-- ProcessLookupError
      |    +-- TimeoutError
      +-- ReferenceError
      +-- RuntimeError
      |    +-- NotImplementedError
      |    +-- RecursionError
      +-- SyntaxError
      |    +-- IndentationError
      |         +-- TabError
      +-- SystemError
      +-- TypeError
      +-- ValueError
      |    +-- UnicodeError
      |         +-- UnicodeDecodeError
      |         +-- UnicodeEncodeError
      |         +-- UnicodeTranslateError
      +-- Warning
           +-- DeprecationWarning
           +-- PendingDeprecationWarning
           +-- RuntimeWarning
           +-- SyntaxWarning
           +-- UserWarning
           +-- FutureWarning
           +-- ImportWarning
           +-- UnicodeWarning
           +-- BytesWarning
           +-- ResourceWarning
  File "<ipython-input-7-e0cfe9de490f>", line 2
    +-- SystemExit
    ^
IndentationError: unexpected indent

3 錯誤的傳遞

# err.py:
def foo(s):
    return 10 / int(s)

def bar(s):
    return foo(s) * 2

def main():
    bar('0')

main()
---------------------------------------------------------------------------

ZeroDivisionError                         Traceback (most recent call last)

<ipython-input-10-1d7596361dc9> in <module>()
      9     bar('0')
     10 
---> 11 main()


<ipython-input-10-1d7596361dc9> in main()
      7 
      8 def main():
----> 9     bar('0')
     10 
     11 main()


<ipython-input-10-1d7596361dc9> in bar(s)
      4 
      5 def bar(s):
----> 6     return foo(s) * 2
      7 
      8 def main():


<ipython-input-10-1d7596361dc9> in foo(s)
      1 # err.py:
      2 def foo(s):
----> 3     return 10 / int(s)
      4 
      5 def bar(s):


ZeroDivisionError: integer division or modulo by zero

4 記錄錯誤

如果不捕獲錯誤,自然可以讓Python解釋器來打印出錯誤堆棧,但程序也被結束了。既然我們能捕獲錯誤,就可以把錯誤堆棧打印出來,然後分析錯誤原因,同時,讓程序繼續執行下去。

Python內置的logging模塊可以非常容易地記錄錯誤信息:

# err_logging.py

import logging

def foo(s):
    return 10 / int(s)

def bar(s):
    return foo(s) * 2

def main():
    try:
        bar('0')
    except Exception as e:
        logging.exception(e)

main()
print('END')#拋出異常後繼續執行此句
ERROR:root:integer division or modulo by zero
Traceback (most recent call last):
  File "<ipython-input-12-143dc3a3d615>", line 13, in main
    bar('0')
  File "<ipython-input-12-143dc3a3d615>", line 9, in bar
    return foo(s) * 2
  File "<ipython-input-12-143dc3a3d615>", line 6, in foo
    return 10 / int(s)
ZeroDivisionError: integer division or modulo by zero


END

上面同樣是錯誤,但拋出異常然後再繼續執行了

5 拋出錯誤

利用raise可以拋出自己設置的錯誤

class FooError(ValueError):#新建一個異常類(選擇好繼承關係就好),可以用於拋出,因爲異常也是一個類
    pass

def foo(s):
    n = int(s)
    if n==0:
        raise FooError('invalid value: %s' % s)
    return 10 / n

foo('0')
---------------------------------------------------------------------------

FooError                                  Traceback (most recent call last)

<ipython-input-15-8887d546fc87> in <module>()
      8     return 10 / n
      9 
---> 10 foo('0')


<ipython-input-15-8887d546fc87> in foo(s)
      5     n = int(s)
      6     if n==0:
----> 7         raise FooError('invalid value: %s' % s)
      8     return 10 / n
      9 


FooError: invalid value: 0

避免重複拋出異常

def foo(s):
    n = int(s)
    if n==0:
        raise ValueError('invalid value: %s' % s)
    return 10 / n

def bar():
    try:
        foo('0')
    except ValueError as e:
        print('ValueError!')
        raise

bar()
ValueError!



---------------------------------------------------------------------------

ValueError                                Traceback (most recent call last)

<ipython-input-16-ae878a7f35c1> in <module>()
     12         raise
     13 
---> 14 bar()


<ipython-input-16-ae878a7f35c1> in bar()
      7 def bar():
      8     try:
----> 9         foo('0')
     10     except ValueError as e:
     11         print('ValueError!')


<ipython-input-16-ae878a7f35c1> in foo(s)
      2     n = int(s)
      3     if n==0:
----> 4         raise ValueError('invalid value: %s' % s)
      5     return 10 / n
      6 


ValueError: invalid value: 0
# err_reraise.py

def foo(s):
#      n = int(s)
#     if n==0:
#         raise ValueError('invalid value: %s' % s)
    return 10 / s

def bar():
    try:
        foo(0)
    except ValueError as e:
        print('ValueError!')
        raise

bar()
---------------------------------------------------------------------------

ZeroDivisionError                         Traceback (most recent call last)

<ipython-input-21-b83fbf519e8b> in <module>()
     14         raise
     15 
---> 16 bar()


<ipython-input-21-b83fbf519e8b> in bar()
      9 def bar():
     10     try:
---> 11         foo(0)
     12     except ValueError as e:
     13         print('ValueError!')


<ipython-input-21-b83fbf519e8b> in foo(s)
      5 #     if n==0:
      6 #         raise ValueError('invalid value: %s' % s)
----> 7     return 10 / s
      8 
      9 def bar():


ZeroDivisionError: integer division or modulo by zero

2 調試

1 斷言assert

def foo(n):
    print type(n)
    assert n != 0,'n is zero!'
    return 10 / n
def main():
    foo(0)
main()
<type 'int'>



---------------------------------------------------------------------------

AssertionError                            Traceback (most recent call last)

<ipython-input-26-4740780af009> in <module>()
      5 def main():
      6     foo(0)
----> 7 main()


<ipython-input-26-4740780af009> in main()
      4     return 10 / n
      5 def main():
----> 6     foo(0)
      7 main()


<ipython-input-26-4740780af009> in foo(n)
      1 def foo(n):
      2     print type(n)
----> 3     assert n != 0,'n is zero!'
      4     return 10 / n
      5 def main():


AssertionError: n is zero!

2 logging

import logging

s = '0'
n = int(s)
logging.info('n = %d' % n)
print(10 / n)
---------------------------------------------------------------------------

ZeroDivisionError                         Traceback (most recent call last)

<ipython-input-27-34acd0c1650a> in <module>()
      4 n = int(s)
      5 logging.info('n = %d' % n)
----> 6 print(10 / n)


ZeroDivisionError: integer division or modulo by zero
import logging
logging.basicConfig(level=logging.INFO)
s = '0'
n = int(s)
logging.info('n = %d' % n)
print(10 / n)
---------------------------------------------------------------------------

ZeroDivisionError                         Traceback (most recent call last)

<ipython-input-28-7501ca4b96cb> in <module>()
      4 n = int(s)
      5 logging.info('n = %d' % n)
----> 6 print(10 / n)


ZeroDivisionError: integer division or modulo by zero

沒發現有啥用

3 pdb

python -m pdb err.py運行程序,可以用於調試代碼

l #查看代碼
n #下一步
p #打印
q #退出
---------------------------------------------------------------------------

NameError                                 Traceback (most recent call last)

<ipython-input-29-27f5dfd249af> in <module>()
----> 1 l #查看代碼
      2 n #下一步
      3 p #打印
      4 q #退出


NameError: name 'l' is not defined

pdb.set_trace()可以在想要的地方設置斷點

3單元測試

就是利用python自帶的unittest模塊,編寫一個測試類,裏面有很多種測試方法,批量的對代碼進行測試,這樣很方便,待代碼修改後還可以繼續調用,很好~~

class Dict(dict):

    def __init__(self, **kw):
        super().__init__(**kw)

    def __getattr__(self, key):
        try:
            return self[key]
        except KeyError:
            raise AttributeError(r"'Dict' object has no attribute '%s'" % key)

    def __setattr__(self, key, value):
        self[key] = value
import unittest
#from mydict import Dict
class TestDict(unittest.TestCase):

    def test_init(self):
        d = Dict(a=1, b='test')
        self.assertEqual(d.a, 1)
        self.assertEqual(d.b, 'test')
        self.assertTrue(isinstance(d, dict))

    def test_key(self):
        d = Dict()
        d['key'] = 'value'
        self.assertEqual(d.key, 'value')

    def test_attr(self):
        d = Dict()
        d.key = 'value'
        self.assertTrue('key' in d)
        self.assertEqual(d['key'], 'value')

    def test_keyerror(self):
        d = Dict()
        with self.assertRaises(KeyError):
            value = d['empty']

    def test_attrerror(self):
        d = Dict()
        with self.assertRaises(AttributeError):
            value = d.empty
#以test開頭的方法就是測試方法,不以test開頭的方法不被認爲是測試方法,測試的時候不會被執行。
if __name__ == '__main__':
    unittest.main()

直接在jupyter notebook裏面運行會報錯,正確的做法是編輯倆個.py文件,然後在運行…test.py那個即可
具體參考:http://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/00143191629979802b566644aa84656b50cd484ec4a7838000

huxiang@shenyong-Opt790:~/work/testvimydict.pyhuxiang@shenyongOpt790: /work/test vi mydict_test.py
huxiang@shenyong-Opt790:~/work/test$ python mydict_test.py

EEEEE

ERROR: test_attr (main.TestDict)

Traceback (most recent call last):
File “mydict_test.py”, line 17, in test_attr
d = Dict()
File “/home/huxiang/work/test/mydict.py”, line 4, in init
super().init(**kw)
TypeError: super() takes at least 1 argument (0 given)

======================================================================

ERROR: test_attrerror (main.TestDict)

Traceback (most recent call last):
File “mydict_test.py”, line 28, in test_attrerror
d = Dict()
File “/home/huxiang/work/test/mydict.py”, line 4, in init
super().init(**kw)
TypeError: super() takes at least 1 argument (0 given)

======================================================================

ERROR: test_init (main.TestDict)

Traceback (most recent call last):
File “mydict_test.py”, line 6, in test_init
d = Dict(a=1, b=’test’)
File “/home/huxiang/work/test/mydict.py”, line 4, in init
super().init(**kw)
TypeError: super() takes at least 1 argument (0 given)

======================================================================

ERROR: test_key (main.TestDict)

Traceback (most recent call last):
File “mydict_test.py”, line 12, in test_key
d = Dict()
File “/home/huxiang/work/test/mydict.py”, line 4, in init
super().init(**kw)
TypeError: super() takes at least 1 argument (0 given)

======================================================================

ERROR: test_keyerror (main.TestDict)

Traceback (most recent call last):
File “mydict_test.py”, line 23, in test_keyerror
d = Dict()
File “/home/huxiang/work/test/mydict.py”, line 4, in init
super().init(**kw)
TypeError: super() takes at least 1 argument (0 given)


Ran 5 tests in 0.001s

FAILED (errors=5)

4 文檔測試

# mydict2.py
class Dict(dict):
    '''
    Simple dict but also support access as x.y style.

    >>> d1 = Dict()
    >>> d1['x'] = 100
    >>> d1.x
    100
    >>> d1.y = 200
    >>> d1['y']
    200
    >>> d2 = Dict(a=1, b=2, c='3')
    >>> d2.c
    '3'
    >>> d2['empty']
    Traceback (most recent call last):
        ...
    KeyError: 'empty'
    >>> d2.empty
    Traceback (most recent call last):
        ...
    AttributeError: 'Dict' object has no attribute 'empty'
    '''
    def __init__(self, **kw):
        super(Dict, self).__init__(**kw)

    def __getattr__(self, key):
        try:
            return self[key]
        except KeyError:
            raise AttributeError(r"'Dict' object has no attribute '%s'" % key)

    def __setattr__(self, key, value):
        self[key] = value

if __name__=='__main__':
    import doctest
    doctest.testmod()

運行什麼結果也沒有,代表代碼是正確的,如果把getattr註釋掉,則會報錯:

# mydict2.py
class Dict(dict):
    '''
    Simple dict but also support access as x.y style.

    >>> d1 = Dict()
    >>> d1['x'] = 100
    >>> d1.x
    100
    >>> d1.y = 200
    >>> d1['y']
    200
    >>> d2 = Dict(a=1, b=2, c='3')
    >>> d2.c
    '3'
    >>> d2['empty']
    Traceback (most recent call last):
        ...
    KeyError: 'empty'
    >>> d2.empty
    Traceback (most recent call last):
        ...
    AttributeError: 'Dict' object has no attribute 'empty'
    '''
    def __init__(self, **kw):
        super(Dict, self).__init__(**kw)

    def __getattr__(self, key):
        try:
            return self[key]
        except KeyError:
            raise AttributeError(r"'Dict' object has no attribute '%s'" % key)

#     def __setattr__(self, key, value):
#         self[key] = value

if __name__=='__main__':
    import doctest
    doctest.testmod()
**********************************************************************
File "__main__", line ?, in __main__.Dict
Failed example:
    d1['y']
Exception raised:
    Traceback (most recent call last):
      File "/home/huxiang/anaconda2/envs/tensorflow/lib/python2.7/doctest.py", line 1315, in __run
        compileflags, 1) in test.globs
      File "<doctest __main__.Dict[4]>", line 1, in <module>
        d1['y']
    KeyError: 'y'
**********************************************************************
1 items had failures:
   1 of   9 in __main__.Dict
***Test Failed*** 1 failures.
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章