自動採点のTips

自動採点の書式

nbgraderのAutograder testsセルにユニットテストを書くだけ。 関数func(x=3)の返り値が1である場合に正解とするなら,以下のように書く。

from nose.tools import eq_
eq_(func(x=3),1)

これを実行したとき,func(x=3)の返り値が不適切なときにエラーが出る。

自動採点に使えるメソッド

以下のメソッドのほとんどはエラーが出た時のメッセージmsg="..."を引数に追加できる。 msgを追加できない場合は後述のようにtryとexceptを使うと良い。

一般

メソッド
assert_true(x) bool(x) is True
assert_false(x) bool(x) is False
assert_is(a, b) a is b
assert_is_not(a, b) a is not b
assert_is_none(x) x is None
assert_is_not_none(x) x is not None
assert_is_instance(a, b) isinstance(a, b)
assert_not_is_instance(a, b) not isinstance(a, b)

数値(int, float)の比較

メソッド alias
assert <expr> ok_(<expr>)
assert_equal(a,b) eq_(a,b) a == b
assert_not_equal(a, b) a != b
assert_almost_equal(a,b) round(a-b, 7) == 0
assert_greater(a, b) a > b
assert_greater_equal(a, b) a >= b
assert_less(a, b) a < b
assert_less_equal(a, b) a <= b

sequence(setなど)に対する判定

setの要素には順番が定義されないので,assert_equal()での判定は不適切。 例えば以下の関数がある。

メソッド
assert_in(a, b) a in b
assert_not_in(a, b) a not in b
assert_count_equal(a, b) a and b have the same elements in the same number, regardless of their order.

リストに対する判定

リスト同士(list, ndarray同士も含む)の比較には,numpy.testingnt.assert_array_equalを使う。

リスト要素を持つリストが,あるリスト要素を含むか(順不同)を確認したいときには以下のような関数を使うとよい。

# check if src involves tgt
def _def _assert_array_involve(src, tgt, msg=""):
    try:
        assert any( np.array_equal(tgt,i) for i in src )
    except:
        _alert(msg)
        raise ValueError(msg)

dictに対する判定

Python unittest - asserting dictionary with listsにのっている以下の方法を使う。dictの要素のtypeによらずに対応できる。

# check if src involves all items in tgt
def _assert_dict_equal(src, tgt, msg=""):
    try:
        assert all( (k,v) in src.items() for (k,v) in tgt.items() )
    except:
        _alert(msg)
        raise ValueError

文字列に対する判定

メソッド
assert_regex(s, r)
assert_not_regex(s, r)

呼び出し

以下のように必要なものだけimportして使っている。

from nose.tools import eq_
from nose.tools import ok_
from nose.tools import assert_in
from nose.tools import assert_equal
from nose.tools import assert_not_equal

他のメソッド

利用できるメソッドは他にも色々ある。

小技のメモ

関数中に,指定した特定の関数が使われているかを確認する

以下は,unittestpatchを利用して,関数show_listenumerate()が利用されているかを確認する例。some_argssome_args_for_enumerateは,show_list()および,show_list内のenumerate()に渡される引数。

from unittest.mock import patch
with patch('__main__.enumerate') as mock_enumerate:
    show_list(some_args)

mock_enumerate.assert_called_once_with(some_args_for_enumerate)

関数中のprint()の出力を評価する

  1. print文が1回だけ実行される場合
  2. print文`が複数回実行される場合(参考)
import sys, io, contextlib

class Data(object):
	pass

@contextlib.contextmanager
def capture_stdout():
	old = sys.stdout
	capturer = io.StringIO()
	data = Data()
	try:
		sys.stdout = capturer
		yield data
	finally:
		sys.stdout = old
		data.result = capturer.getvalue()

ここまでが宣言部分。以下が利用方法。

with capture_stdout() as capture:
	print("hello")   # ここに,監視する関数を列挙する。
	print("goodbye")

res=capture.result
ans="hello\n\
goodbye"
assert res.replace(" ","").strip()==ans.replace(" ","").strip() # usually better to remove spaces, and spaces/tabs/\n at the end

乱数を使った関数の動作を確認する

関数の返り値が乱数を使って生成される場合,正解か否かの判定は難しいことも多いが,少なくとも乱数で生成されているかどうかの確認は簡単にできる。

result=[rnd_func() for i in range(20)]
result2=[rnd_func() for i in range(20)]
assert_not_equal(result,result2)

さらに,平均値や分散, max, minを使った色々な判定も可能。 ランダムに"stone",“paper”,“scissors"を発生させるような関数なら,上記の確認後に,以下のような確認をするのも良い。

assert "stone" in result
assert "paper" in result
assert "scissors" in result

エラーメッセージをtryを使って表示する方法

エラーメッセージは,unittest関数の引数"msg"でも指定できるが, 背景色をつけたり等,メッセージを分かりやすくするにはtry&exceptを使うと便利。

from nose.tools import eq_

try:
	eq_(func(x=3),1)
except:
	display('func(x=3)の返り値は1でないといけません')
	raise

上記のdisplay()で表示される内容を,もっと目立たせるためには,以下のようにエラー メッセージ表示用の関数を作ると良い。

from IPython.display import HTML
from nose.tools import eq_

def _alert(msg):
        display(HTML('<div class="alert alert-danger">{msg}</div>'.format(msg=msg)))

try:
	eq_(func(x=3),1)
except:
	_alert('func(x=3)の返り値は1でないといけません')
	raise

注意点

採点対象になっている関数が,その後のセルの評価に影響するときは要注意。評価時には受講生の内容に全て置き換わるので,受講生に不適切に定義された関数がその後の評価を不適切なものにしてしまうことがある。

おまけ

  • jharmrick/nbgrader-demo
    • jupyter notebook/nbgraderを使うための,学生ガイダンス用notebookなどがある。
  • bootstrap-theme.css
    • nbgraderのcss
    • 以下のalert classを使うと,Markdownセルの背景に色を付けることもできる。
      • <div class="alert alert-success">緑色
      • <div class="alert alert-info"> 青色
      • <div class="alert alert-warning"> 黄色
      • <div class="alert alert-danger"> 赤色
  • 自動採点のときにjupyter notebookの実行は途中のセルまでで停止する方法
    • 停止したいセルに以下を入力(情報源)
raise SystemExit("Stop right here!")