エラーの分類 （python3.x)とDebuggingメモ

[過去](http://qiita.com/weedslayer/items/1a4c5131ef88243f50ff)にも少しだけ触れたが、もっと分かりやすく解説してくれている記事を見つけたので復習の意味も込めて。[大先輩](http://albertwu.org/cs61a/notes/debugging)のブログより転載, aka all credit belongs to our legendary cs alumni

##Traceback message

```
Traceback (most recent call last):
  File "<pyshell#29>", line 3 in <module>
    result = buggy(5)
  File <pyshell#29>", line 5 in buggy
    return f + x
TypeError: unsupported operand type(s) for +: 'function' and 'int'
```

traceback messageのはエラーが起きた時系列順に経緯を辿ってプリントしてくれる。一番下が最も新しい。traceback messageは２つで一組の構成になっている。１つ目`File "<pyshell#29>", line 3 in <module>`はエラーを含んでいるファイルの名前、どこに（何番目のライン）にそのエラーがあるのか、そしてそのエラーを含んでいる関数名の3つを`File "<file name>", line <number>, in <function>`というフォーマットで表している。２つ目`result = buggy(5)`はそのままだがその実際のコードを表示している。

##Error messages
一番最後の行に書かれているのがerror messageである。error message`<error type>: <error message>`はエラーの種類（syntaxerror, typeerror etc）とエラーの詳細のフォーマットで記載されている。

##Debugging

###Doctestでとりあえず動かしてみる

```
def foo(x):
    """A random function.

    >>> foo(4)
    4
    >>> foo(5)
    5
    """
```
terminal上より`python3 -m doctest file.py`
`python3 -m doctest file.py -v`を使えばどのテストがパスしてどこが引っかかったかを教えてくれる。

通例としては最初の一行目で具体的にその関数が何をするのかを書き、一行ブランクを空ける。詳細な説明は[こちら](https://docs.python.org/3/library/doctest.html)。

もし`doctest`の対象がsingle statement(=a single line that doesn't end in a `:`)の場合は"run_docstring_examples"と呼ばれる`doctest`関数をつかう。この関数は3つの引数を持ちあわせている。1つめはテストしたい関数名、2つめは`global()`(=a built-in function that returns the global environment)、そして3つめが`True`(to indicate that we would like "verbose" output: a catalog of all tests run.)。ちなみにsingle statementをテストすることを__unit test__と呼ぶ。

>
_Exhaustive unit testing is a hallmark of good program design._
- _良いプログラムデザインは包括的なunit testによって保証される。_

それ以外にも自分自身でテスト関数を作ったり、新たにdoctestを作るってもよい。

>
_Always run these doctests. Submitting an assignment without running doctests even once is practically throwing points away._

マジかよ。それは知らなかった（笑）

###要所要所にprint()を挟んで経過を確認

```
def foo(x):
    result = some_function(x)
    return result // 5
```

というコードを書いたとしてどこにエラーがあるのかわからない場合はとりあえず結果を`print`させてみる。

```
def foo(x):
    result = some_function(x)
    print('result is', result)
    return other_function(result)
```

そしてできればただ`print`させるのではなく一言加えておくと忘れずに済む

```
print(tmp)   # harder to keep track
print('tmp was this:', tmp)  # easier
```

while loopの後に入れるのも効果的

```
i = 0
while i < n:
    i += func(i)
    print('i is', i)
```

テストが終わったら`print`を消しておくのを忘れずに

##Long-term debugging

プロジェクト単位になってくると時にはデバグを瞬時に行わないほうがいい場合もある。そういうときは以下のようにしておくと便利

```
debug = True

def foo(n):
i = 0
while i < n:
    i += func(i)
    if debug:
        print('i is', i)
```

もし毎回結果表示されるのが嫌ならば`false`に変えてしまえばよい。こういった使い方をする変数を`flag`と呼ぶ。

##Types of error

###Syntax Error
原因：

- コードに問題あり

```
 File "file name", line number
    def incorrect(f)
                    ^
SyntaxError: invalid syntax
```

`^`が不適切なシンタックスを含んでいるコードを示している。何がおかしいのかは表示されないがその原因がどこにあるかは教えてくれる。Pythonはコードを走らせる前にまずこの`syntax error`がないかを確認する。

###Indentation Error
原因: 

- コードのラインのインデントに問題有り

```
 File "file name", line number
    print('improper indentation')
IndentationError: unindent does not match any outer indentation level
```

不適切にインデントがなされているところを表示している。スペースとタブの機能の区別をはっきりと。

###TypeError

原因：

- operand（max, mul etc..）とprimitive operator（数字など）の使い方が不適切の場合。

```
TypeError: unsupported operand type(s) for +: 'function' and 'int'
```

- 関数とオブジェクトがごっちゃになっている。

```
>>> square = 3
>>> square(3)
Traceback (most recent call last):
  ...
TypeError: 'int' object is not callable`
```

- 引数の数に問題有り。

```
>>> add(3)
Traceback (most recent call last):
  ...
TypeError: add expected 2 arguments, got 1
```

###NameError

原因：
- 定義されていない変数を使っているもしくは関数 

```
File "file name", line number
  y = x + 3
NameError: global name 'x' is not defined
```

notes: ここで`global name`と記されている理由はPythonがエラーを含む関数のローカル領域から最終的にグローバル領域に入っていくためである。

###IndexError

原因：
- sequence(tuple, list, string)の渡せる数の限界を超えている。

```
File "file name", line number
  x[100]
IndexError: tuple index out of range
```

##よくあるバグ

###simple spelling mistakes

`hello` ≠ `Hello` ≠ `HELLO`ということ。`NameError`と出てきた時はスペリングにミスがあるのかも。または間違えたスペリングで変数や関数を定義してしまっている場合もある。

###Missing Parentheses
そのまんま括弧のつけ忘れなど

```
def fun():
    return foo(bar()   # missing a parenthesis here

fun()
``` 

この場合、pythonは`SyntaxError`と称し、括弧の問題がある行の一つ後の行を指す。

###Missing close quotes
pythonが具体的に教えてくれるので見つけるのも簡単。

```
File "file name", line "number"
  return 'hi
           ^
SyntaxError: EOL while scanning string literal
```

EOL = "end of line"

###= (assignment) vs. == (equality test)

`=`は変数を引き渡すときに、`==`は変数が同等かどうかを確かめるために使う。典型的なエラーは

```
if x = 3: # when should be "==" instead
```

###Infinite Loops

whileループはそのコンディションが達成されるまで回り続けるので陥りやすい。

```
i = 0
while i < 10:
    print(i)
```

```
i, n = 0, 0
while i < 10:
    print(i)
    n += 1
```