Help us understand the problem. What is going on with this article?

Codeforces用Pythonテンプレート ~手動テスト~

More than 5 years have passed since last update.

背景

問題の背景は意外と読んでて楽しい。

久しぶりに競プロ熱が高まってきたので、ここ数日リハビリとして簡単な問題を解いています。
折角なので、昔使っていたC++ + Emacsの環境を改め、Python + PyCharmにしてみました。

その際、テストが(それなりに)簡単に追加できるテンプレートが出来上がったので、ここに掲載します。
以下、作成したテンプレの説明です。

対象と制約

まず制約条件をチェックするのは競プロの鉄則。

今回は、数ある競プロサイトの中でも、Codeforcesを対象とします。
Codeforcesには、以下の特徴があります。
1. 標準入出力でやりとりする
2. テストケースは多くて3つくらいしか用意されていない
3. ファイルをそのまま提出することができる

まず、用意されているテストケースが少ないため、自作テストをいかに楽に追加できるかが肝になります。
(逆に言えば、問題文からテストケースを自動生成する必要性は薄い、ということです)

その際にネックになるのが1番の特徴です。
関数の引数と返り値でやりとりする(TopCoderなどのコンテスト)場合は、単体テストを普通に書けばよいため、IDEのサポートも受けやすいです。
しかし、標準入出力はなんだかあんまりサポートされておらず、jUnitなどでもテストしにくい印象です。
そのため、少し面倒な方法を取る必要があります。

また、ファイルをそのまま提出すると楽できるので、そのまま提出できる形にしたいです。

作ったテンプレ

型に嵌る。

前置きが長くなりましたが、作ったテンプレをここに貼っておきます。
2ファイルがありますが、
1つはコードを記述しそのまま提出できるファイル、
もう1つはテストを記述するファイルです。

まず、コードを記述するファイル。
見ちゃダメ!(CV:ウメス)以降は、問題を解いている最中見る必要の無い箇所です。

main.py
# -*- coding: utf-8 -*-
import sys
# 必要なimportを増やす


class Solver(object):

    def run(self):
        ### ここに回答を記載 ###


####################################
# ここからは見ちゃダメ!

### ここにライブラリを記載 ###

if __name__ == '__main__':
    Solver().run()

次に、テストを書くファイル。

test_solver.py
# -*- coding: utf-8 -*-
from unittest import TestCase


data = [
    (
        """
入力1
""".strip()
        ,
        """
回答1
""".strip()
    ),

    (
        """
入力2
""".strip()
        ,
        """
回答2
""".strip()
    ),
]


########################################
# ここからは見ちゃダメ!

class TestSolver(TestCase):
    def test_run(self):
        import os
        import sys
        from StringIO import StringIO
        import main

        for i, testdata in enumerate(data):
            print(str(i) + " test...")
            saved_stdout = sys.stdout
            try:
                with open("temp_data.txt", "w+") as temp:
                    temp.write(testdata[0])

                fd = os.open("temp_data.txt", os.O_RDONLY)
                os.dup2(fd, sys.stdin.fileno())

                out = StringIO()
                sys.stdout = out

                main.Solver().run()
                output = out.getvalue().strip()

                self.assertEquals(output, testdata[1])
            finally:
                sys.stdout = saved_stdout

説明

test_solver.pyのTestSolver.test_run()を実行すると、ファイルの先頭辺りで定義したテストケース郡が走ります。
この関数では、
標準入力をファイルから受け取り、
標準出力を変数にリダイレクト
しています。
これにより、標準入力と標準出力を置き換えて扱うことができます。

工夫点

テストケースはヒアドキュメントを使っているため、コピペで簡単に追加できます。
マウスの練度は不要です。

標準入力はファイルを経由しなくてもいいんじゃない?と思いましたが、何故か出来なかったので諦めました(詳しい人plz)。
また、tempfileを使えば書けそうな所をわざわざ実ファイルに書き出していますが、Windows?とかいうマイナーOSではtempfileの読み込み権限が無いとかで怒られてしまいます。
回避するのも面倒…もとい出来なかったので、諦めました(詳しい人plz)。

使い方

ほとんど見ての通りです。
基本的にファイルの上の辺りを編集すれば済むようにしています。

まとめ

recursive call

テンプレートはただのファイルなので、お好きなエディタと組み合わせて使えます。
ゆとりコーダーとしては、PyCharmが楽でいいです。
自動補完もありますし、実行もショートカットキーで楽ちんです。
無論、EmacsやVimと組み合わせて慣れたエディタで戦うのも良いでしょう。
Emacsが先に来ている事に一切意味はございません。

最後に

こうすればもっと便利やで!という知見をお持ちの方は、ぜひコメントください。

esplo
日頃からぷるぷるしています。直方体に近いです。
https://blog.esplo.net/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした