📖 Python自習O_9o0 目次だぜ(^~^) - 目次
📖 Python自習O0o0 クラスを動的に読み込もうぜ(^~^) - Dimport クラス
├── 📂 src
│ ├── 📂 dimport
│ │ └── 📄 __init__.py
│ ├── 📂 hello
│ │ └── 📄 __init__.py
│ └── 📄 __init__.py
└── 📂 tests
├── 📄 dimport_test.py
├── 📄 dimport_test2.py
└── 📄 hello_test.py
Input:
ABC123DEF456GHI
Output:
["ABC", "123", "DEF", "456", "GHI"]
「 input 無視して output をそのまま出せばいいのでは?」
「 テストを書いて置いたから、これに合うようにコードを書きなさい」
├── 📂 tests
│ └── 📂 nonnumsv
│ └── 📂 o1o0g1o1o0
👉 │ └── 📄 quest.py
├── 📄 .gitignore
├── 📄 LICENSE
└── 📄 README.md
class Questioner:
"""出題者"""
def make_quiz(self):
"""答えのある問い作成"""
return "ABC123DEF456GHI"
def check(self, answer, quiz):
"""答え合わせ
Returns
-------
str
Error message or None
"""
err_list = []
if answer is None:
err_list.append("[Error] answer is none")
else:
vec_size = len(answer)
if vec_size != 5:
err_list.append(
f"[Error] the size is different. size:{vec_size}")
if answer != ["ABC", "123", "DEF", "456", "GHI"]:
err_list.append(
f"[Error] the response is different. vec:{answer}")
if 0 < len(err_list):
return "\n".join(err_list)
else:
return None
├── 📂 tests
│ ├── 📂 general
│ │ └── 📂 o1o0g1o1o0
👉 │ │ └── 📄 test.py
│ └── 📂 nonnumsv
│ └── 📂 o1o0g1o1o0
│ └── 📄 quest.py
├── 📄 .gitignore
├── 📄 LICENSE
└── 📄 README.md
"""
Example
-------
python -m tests.general.o1o0g1o1o0.test --qm tests.nonnumsv.o1o0g1o1o0.quest --qc Questioner --am src.nonnumsv.o1o0g1o2o0 --ac Answerer
"""
import argparse
from src.dimport import Dimport # Dynamic class import
# Command line arguments
ap = argparse.ArgumentParser()
ap.add_argument('--qm', help='questioner module')
ap.add_argument('--qc', help='questioner class')
ap.add_argument('--am', help='answerer module')
ap.add_argument('--ac', help='answerer class')
args = ap.parse_args()
# Plan
Questioner = Dimport.load(args.qm, args.qc)
quest = Questioner()
quiz = quest.make_quiz()
print(f"quiz:{quiz}")
# Do
Answerer = Dimport.load(args.am, args.ac)
answer = Answerer.to_answer(quiz)
print(f"answer:{answer}")
# Check
err = quest.check(answer, quiz)
if err is None:
print("correct!")
else:
print(err)
├── 📂 tests
│ ├── 📂 general
│ │ └── 📂 o1o0g1o1o0
│ │ └── 📄 test.py
│ ├── 📂 nonnumsv
│ │ └── 📂 o1o0g1o1o0
│ │ └── 📄 quest.py
👉 │ └── 📄 __init__.py
├── 📄 .gitignore
├── 📄 LICENSE
└── 📄 README.md
from .nonnumsv.o1o0g1o1o0.quest import Questioner as QuestionerO1o0g1o1o0
# -------------------------- ---------- --------------------
# 1 2 3
# 1. 同じディレクトリーからの相対パス
# 2. クラス
# 3. クラスの別名
「 これでどこかに Answerer
クラスを作って to_answer
メソッドを付けてくれれば テストできるわよ」
├── 📂 src
│ └── 📂 nonnumsv
│ └── 📂 o1o0g1o2o0
👉 │ └── 📄 __init__.py
├── 📂 tests
│ ├── 📂 general
│ │ └── 📂 o1o0g1o1o0
│ │ └── 📄 test.py
│ ├── 📂 nonnumsv
│ │ └── 📂 o1o0g1o1o0
│ │ └── 📄 quest.py
│ └── 📄 __init__.py
├── 📄 .gitignore
├── 📄 LICENSE
└── 📄 README.md
class Answerer:
"""Non-numeric separated value"""
@staticmethod
def to_answer(quiz):
return []
├── 📂 src
│ └── 📂 nonnumsv
│ ├── 📂 o1o0g1o2o0
│ │ └── 📄 __init__.py
👉 │ └── 📄 __init__.py
├── 📂 tests
│ ├── 📂 general
│ │ └── 📂 o1o0g1o1o0
│ │ └── 📄 test.py
│ ├── 📂 nonnumsv
│ │ └── 📂 o1o0g1o1o0
│ │ └── 📄 quest.py
│ └── 📄 __init__.py
├── 📄 .gitignore
├── 📄 LICENSE
└── 📄 README.md
from .o1o0g1o2o0 import Answerer as AnswererO1o0g1o2o0
# ----------- -------- ------------------
# 1 2 3
# 1. 同じディレクトリーにある o1o0g1o2o0 ディレクトリー
# 2. クラス
# 3. クラスの別名
「 👇 前のレッスンで作った、以下の既存ファイルに1行追加してくれだぜ」
├── 📂 src
│ ├── 📂 nonnumsv
│ │ ├── 📂 o1o0g1o2o0
│ │ │ └── 📄 __init__.py
│ │ └── 📄 __init__.py
👉 │ └── 📄 __init__.py
├── 📂 tests
│ ├── 📂 general
│ │ └── 📂 o1o0g1o1o0
│ │ └── 📄 test.py
│ ├── 📂 nonnumsv
│ │ └── 📂 o1o0g1o1o0
│ │ └── 📄 quest.py
│ └── 📄 __init__.py
├── 📄 .gitignore
├── 📄 LICENSE
└── 📄 README.md
# ...略...
from .nonnumsv.o1o0g1o2o0 import Answerer
Input:
python -m tests.general.o1o0g1o1o0.test --qm tests.nonnumsv.o1o0g1o1o0.quest --qc Questioner --am src.nonnumsv.o1o0g1o2o0 --ac Answerer
Output:
quiz:ABC123DEF456GHI
answer:[]
[Error] the size is different. size:0
[Error] the response is different. vec:[]
├── 📂 src
│ ├── 📂 nonnumsv
│ │ ├── 📂 o1o0g1o2o0
│ │ │ └── 📄 __init__.py
│ │ ├── 📂 o1o0g1o3o0
👉 │ │ │ └── 📄 __init__.py
│ │ └── 📄 __init__.py
│ └── 📄 __init__.py
├── 📂 tests
│ ├── 📂 general
│ │ └── 📂 o1o0g1o1o0
│ │ └── 📄 test.py
│ ├── 📂 nonnumsv
│ │ └── 📂 o1o0g1o1o0
│ │ └── 📄 quest.py
│ └── 📄 __init__.py
├── 📄 .gitignore
├── 📄 LICENSE
└── 📄 README.md
"""
Example
-------
python -m tests.general.o1o0g1o1o0.test --qm tests.nonnumsv.o1o0g1o1o0.quest --qc Questioner --am src.nonnumsv.o1o0g1o3o0 --ac Answerer
"""
class Answerer:
"""Non-numeric separated value"""
@staticmethod
def to_answer(text):
return ["ABC", "123", "DEF", "456", "GHI"]
├── 📂 src
│ ├── 📂 nonnumsv
│ │ ├── 📂 o1o0g1o2o0
│ │ │ └── 📄 __init__.py
│ │ ├── 📂 o1o0g1o3o0
│ │ │ └── 📄 __init__.py
👉 │ │ └── 📄 __init__.py
│ └── 📄 __init__.py
├── 📂 tests
│ ├── 📂 general
│ │ └── 📂 o1o0g1o1o0
│ │ └── 📄 test.py
│ ├── 📂 nonnumsv
│ │ └── 📂 o1o0g1o1o0
│ │ └── 📄 quest.py
│ └── 📄 __init__.py
├── 📄 .gitignore
├── 📄 LICENSE
└── 📄 README.md
# ...略...
from .o1o0g1o3o0 import Answerer as AnswererO1o0g1o3o0
# ...略...
「 モジュールの検索パスを 手動で書くの めんどくさいけど 仕方ない」
Input:
python -m tests.general.o1o0g1o1o0.test --qm tests.nonnumsv.o1o0g1o1o0.quest --qc Questioner --am src.nonnumsv.o1o0g1o3o0 --ac Answerer
Output:
quiz:ABC123DEF456GHI
answer:['ABC', '123', 'DEF', '456', 'GHI']
correct!
「 今までは 練習の枠組みを用意したわけだぜ。
ここからが本番だぜ」
├── 📂 src
│ ├── 📂 nonnumsv
│ │ ├── 📂 o1o0g1o2o0
│ │ │ └── 📄 __init__.py
│ │ ├── 📂 o1o0g1o3o0
│ │ │ └── 📄 __init__.py
│ │ ├── 📂 o1o0g1o4o0
👉 │ │ │ └── 📄 __init__.py
│ │ └── 📄 __init__.py
│ └── 📄 __init__.py
├── 📂 tests
│ ├── 📂 general
│ │ └── 📂 o1o0g1o1o0
│ │ └── 📄 test.py
│ ├── 📂 nonnumsv
│ │ └── 📂 o1o0g1o1o0
│ │ └── 📄 quest.py
│ └── 📄 __init__.py
├── 📄 .gitignore
├── 📄 LICENSE
└── 📄 README.md
"""
Example
-------
python -m tests.general.o1o0g1o1o0.test --qm tests.nonnumsv.o1o0g1o1o0.quest --qc Questioner --am src.nonnumsv.o1o0g1o4o0 --ac Answerer
"""
import re
class Answerer:
"""Non-numeric separated value"""
# * `^` - 行頭
# * `( )` - キャプチャーグループ
# * `[A-Z]` - 大文字のAからZ
# * `[ ]+` - 1回以上
# * `[0-9]` - 半角数字の0から9
# * `$` - 行末
__pat = re.compile(r"^([A-Z]+)([0-9]+)([A-Z]+)([0-9]+)([A-Z]+)$")
@staticmethod
def to_answer(quiz):
m = Answerer.__pat.match(quiz)
if m:
# `m.group( )` - 引数の数はパターンの括弧の位置に対応
return [m.group(1), m.group(2), m.group(3), m.group(4), m.group(5)]
return None
├── 📂 src
│ ├── 📂 nonnumsv
│ │ ├── 📂 o1o0g1o2o0
│ │ │ └── 📄 __init__.py
│ │ ├── 📂 o1o0g1o3o0
│ │ │ └── 📄 __init__.py
│ │ ├── 📂 o1o0g1o4o0
│ │ │ └── 📄 __init__.py
👉 │ │ └── 📄 __init__.py
│ └── 📄 __init__.py
├── 📂 tests
│ ├── 📂 general
│ │ └── 📂 o1o0g1o1o0
│ │ └── 📄 test.py
│ ├── 📂 nonnumsv
│ │ └── 📂 o1o0g1o1o0
│ │ └── 📄 quest.py
│ └── 📄 __init__.py
├── 📄 .gitignore
├── 📄 LICENSE
└── 📄 README.md
# ...略...
from .o1o0g1o4o0 import Answerer as AnswererO1o0g1o4o0
# ...略...
Input:
python -m tests.general.o1o0g1o1o0.test --qm tests.nonnumsv.o1o0g1o1o0.quest --qc Questioner --am src.nonnumsv.o1o0g1o4o0 --ac Answerer
Output:
quiz:ABC123DEF456GHI
answer:['ABC', '123', 'DEF', '456', 'GHI']
correct!
├── 📂 src
│ ├── 📂 nonnumsv
│ │ ├── 📂 o1o0g1o2o0
│ │ │ └── 📄 __init__.py
│ │ ├── 📂 o1o0g1o3o0
│ │ │ └── 📄 __init__.py
│ │ ├── 📂 o1o0g1o4o0
│ │ │ └── 📄 __init__.py
│ │ └── 📄 __init__.py
│ └── 📄 __init__.py
├── 📂 tests
│ ├── 📂 general
│ │ └── 📂 o1o0g1o1o0
│ │ └── 📄 test.py
│ ├── 📂 nonnumsv
│ │ ├── 📂 o1o0g1o1o0
│ │ │ └── 📄 quest.py
│ │ └── 📂 o1o0g1o5o0
👉 │ │ └── 📄 quest.py
│ └── 📄 __init__.py
├── 📄 .gitignore
├── 📄 LICENSE
└── 📄 README.md
"""
Example
-------
python -m tests.general.o1o0g1o1o0.test --qm tests.nonnumsv.o1o0g1o5o0.quest --qc Questioner --am src.nonnumsv.o1o0g1o4o0 --ac Answerer
"""
import random
class Questioner:
"""出題者"""
def make_quiz(self):
"""答えのある問い作成"""
quiz = """!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~"""
# Shuffle
quiz = ''.join(random.sample(quiz, len(quiz)))
return quiz
def check(self, answer, quiz):
"""答え合わせ
Returns
-------
str
Error message or None
"""
err_list = []
if answer is None:
err_list.append("[Error] answer is none")
elif len(answer) < 2:
err_list.append(
f"[Error] answer length is small. len:{len(answer)} (< 2)")
else:
is_error = False
# もとの文字列と一致するかチェック
text2 = ''.join(answer)
if text2 != quiz:
is_error = True
err_list.append("[Error] the string is different")
err_list.append(f"> actual :{text2}")
err_list.append(f"> expected:{quiz}")
if not is_error:
# 数字,非数字が 交互かチェック
is_prev_numeric = answer[0].isnumeric()
for i in range(1, len(answer)):
is_numeric = answer[i].isnumeric()
if is_prev_numeric == is_numeric:
# Error
is_error = True
break
is_prev_numeric = is_numeric
if 0 < len(err_list):
return "\n".join(err_list)
else:
return None
├── 📂 src
│ ├── 📂 nonnumsv
│ │ ├── 📂 o1o0g1o2o0
│ │ │ └── 📄 __init__.py
│ │ ├── 📂 o1o0g1o3o0
│ │ │ └── 📄 __init__.py
│ │ ├── 📂 o1o0g1o4o0
│ │ │ └── 📄 __init__.py
│ │ └── 📄 __init__.py
│ └── 📄 __init__.py
├── 📂 tests
│ ├── 📂 general
│ │ └── 📂 o1o0g1o1o0
│ │ └── 📄 test.py
│ ├── 📂 nonnumsv
│ │ ├── 📂 o1o0g1o1o0
│ │ │ └── 📄 quest.py
│ │ └── 📂 o1o0g1o5o0
│ │ └── 📄 quest.py
👉 │ └── 📄 __init__.py
├── 📄 .gitignore
├── 📄 LICENSE
└── 📄 README.md
# ...略...
from .nonnumsv.o1o0g1o5o0.quest import Questioner as QuestionerO1o0g1o5o0
# ...略...
Input:
python -m tests.general.o1o0g1o1o0.test --qm tests.nonnumsv.o1o0g1o5o0.quest --qc Questioner --am src.nonnumsv.o1o0g1o4o0 --ac Answerer
Output:
quiz:8pyi0tVKZ"n|/hec\+>=BjIdS<),Pvb%3'_ax-sA?;N2#FH76&T]5[MYk(RJE`oluLO:z{DXg}U4m1rfQw@.*$C!WGq^9~
answer:None
[Error] answer is none
├── 📂 src
│ ├── 📂 nonnumsv
│ │ ├── 📂 o1o0g1o2o0
│ │ │ └── 📄 __init__.py
│ │ ├── 📂 o1o0g1o3o0
│ │ │ └── 📄 __init__.py
│ │ ├── 📂 o1o0g1o4o0
│ │ │ └── 📄 __init__.py
│ │ ├── 📂 o1o0g1o6o0
👉 │ │ │ └── 📄 __init__.py
│ │ └── 📄 __init__.py
│ └── 📄 __init__.py
├── 📂 tests
│ ├── 📂 general
│ │ └── 📂 o1o0g1o1o0
│ │ └── 📄 test.py
│ ├── 📂 nonnumsv
│ │ ├── 📂 o1o0g1o1o0
│ │ │ └── 📄 quest.py
│ │ └── 📂 o1o0g1o5o0
│ │ └── 📄 quest.py
│ └── 📄 __init__.py
├── 📄 .gitignore
├── 📄 LICENSE
└── 📄 README.md
"""
Example
-------
python -m tests.general.o1o0g1o1o0.test --qm tests.nonnumsv.o1o0g1o5o0.quest --qc Questioner --am src.nonnumsv.o1o0g1o6o0 --ac Answerer
"""
import re
class Answerer:
"""Non-numeric separated value"""
# * `^` - 文の始端
# * `\d` - 半角数字
# * `\d+` - 半角数字(1つ以上)
# * `( )` - キャプチャーグループ
__pat_num = re.compile(r"^(\d+)")
# * `\D` - 半角数字以外
__pat_nonnum = re.compile(r"^(\D+)")
@staticmethod
def to_answer(quiz):
answer = []
start = 0
# 数字列か?
m = Answerer.__pat_num.match(quiz[start:])
if m:
# 数字列だ
token = m.group(1)
answer.append(token)
start += len(token)
while True:
# 非数字の文字列か?
m = Answerer.__pat_nonnum.match(quiz[start:])
if m is None:
break
# 非数字の文字列だ
token = m.group(1)
answer.append(token)
start += len(token)
# 数字列か?
m = Answerer.__pat_num.match(quiz[start:])
if m is None:
break
# 数字列だ
token = m.group(1)
answer.append(token)
start += len(token)
return answer
├── 📂 src
│ ├── 📂 nonnumsv
│ │ ├── 📂 o1o0g1o2o0
│ │ │ └── 📄 __init__.py
│ │ ├── 📂 o1o0g1o3o0
│ │ │ └── 📄 __init__.py
│ │ ├── 📂 o1o0g1o4o0
│ │ │ └── 📄 __init__.py
│ │ ├── 📂 o1o0g1o6o0
│ │ │ └── 📄 __init__.py
👉 │ │ └── 📄 __init__.py
│ └── 📄 __init__.py
├── 📂 tests
│ ├── 📂 general
│ │ └── 📂 o1o0g1o1o0
│ │ └── 📄 test.py
│ ├── 📂 nonnumsv
│ │ ├── 📂 o1o0g1o1o0
│ │ │ └── 📄 quest.py
│ │ └── 📂 o1o0g1o5o0
│ │ └── 📄 quest.py
│ └── 📄 __init__.py
├── 📄 .gitignore
├── 📄 LICENSE
└── 📄 README.md
# ...略...
from .o1o0g1o6o0 import Answerer as AnswererO1o0g1o6o0
# ...略...
Input:
python -m tests.general.o1o0g1o1o0.test --qm tests.nonnumsv.o1o0g1o5o0.quest --qc Questioner --am src.nonnumsv.o1o0g1o6o0 --ac Answerer
Output:
quiz:n*;7'Ip{DKr\A6c&SuW"T=U^)3#PxwN0h$]?La!zsV/1[YjJ49tZ`dRO(_Cm,glef5.%2bkXGQqy|~-Fi8v:BM}>oE<+H@
answer:['n*;', '7', "'Ip{DKr\\A", '6', 'c&SuW"T=U^)', '3', '#PxwN', '0', 'h$]?La!zsV/', '1', '[YjJ', '49', 'tZ`dRO(_Cm,glef', '5', '.%', '2', 'bkXGQqy|~-Fi', '8', 'v:BM}>oE<+H@']
correct!
「 じゃあ、わざと答えの一部を スワップ(入れ替え)して 答えればいいんだぜ」
├── 📂 src
│ ├── 📂 nonnumsv
│ │ ├── 📂 o1o0g1o2o0
│ │ │ └── 📄 __init__.py
│ │ ├── 📂 o1o0g1o3o0
│ │ │ └── 📄 __init__.py
│ │ ├── 📂 o1o0g1o4o0
│ │ │ └── 📄 __init__.py
│ │ ├── 📂 o1o0g1o6o0
│ │ │ └── 📄 __init__.py
│ │ ├── 📂 o1o0g1o7o0
👉 │ │ │ └── 📄 __init__.py
│ │ └── 📄 __init__.py
│ └── 📄 __init__.py
├── 📂 tests
│ ├── 📂 general
│ │ └── 📂 o1o0g1o1o0
│ │ └── 📄 test.py
│ ├── 📂 nonnumsv
│ │ ├── 📂 o1o0g1o1o0
│ │ │ └── 📄 quest.py
│ │ └── 📂 o1o0g1o5o0
│ │ └── 📄 quest.py
│ └── 📄 __init__.py
├── 📄 .gitignore
├── 📄 LICENSE
└── 📄 README.md
"""
Example
-------
python -m tests.general.o1o0g1o1o0.test --qm tests.nonnumsv.o1o0g1o5o0.quest --qc Questioner --am src.nonnumsv.o1o0g1o7o0 --ac Answerer
"""
import re
import random
from ..o1o0g1o6o0 import Answerer as AnswererG1o6o0
# ------------ -------- --------------
# 1 2 3
# 1. 隣のフォルダー
# 2. クラス
# 3. 別名
class Answerer:
"""Non-numeric separated value"""
# * `^` - 文の始端
# * `\d` - 半角数字
# * `\d+` - 半角数字(1つ以上)
# * `( )` - キャプチャーグループ
__pat_num = re.compile(r"^(\d+)")
# * `\D` - 半角数字以外
__pat_nonnum = re.compile(r"^(\D+)")
@staticmethod
def to_answer(quiz):
answer = AnswererG1o6o0.to_answer(quiz)
# 要素数
length = len(answer)
# 要素が2個に満たなければスワップできません
if length <= 2:
raise ValueError(f"Couldn't swap. length:{length}")
# ランダムなインデックスを2つ取得
i1 = random.randint(1, length-1)
i2 = random.randint(0, i1-1)
# スワップ
temp = answer[i1]
answer[i1] = answer[i2]
answer[i2] = temp
return answer
├── 📂 src
│ ├── 📂 nonnumsv
│ │ ├── 📂 o1o0g1o2o0
│ │ │ └── 📄 __init__.py
│ │ ├── 📂 o1o0g1o3o0
│ │ │ └── 📄 __init__.py
│ │ ├── 📂 o1o0g1o4o0
│ │ │ └── 📄 __init__.py
│ │ ├── 📂 o1o0g1o6o0
│ │ │ └── 📄 __init__.py
│ │ ├── 📂 o1o0g1o7o0
│ │ │ └── 📄 __init__.py
👉 │ │ └── 📄 __init__.py
│ └── 📄 __init__.py
├── 📂 tests
│ ├── 📂 general
│ │ └── 📂 o1o0g1o1o0
│ │ └── 📄 test.py
│ ├── 📂 nonnumsv
│ │ ├── 📂 o1o0g1o1o0
│ │ │ └── 📄 quest.py
│ │ └── 📂 o1o0g1o5o0
│ │ └── 📄 quest.py
│ └── 📄 __init__.py
├── 📄 .gitignore
├── 📄 LICENSE
└── 📄 README.md
# ...略...
from .o1o0g1o7o0 import Answerer as AnswererO1o0g1o7o0
# ...略...
Input:
python -m tests.general.o1o0g1o1o0.test --qm tests.nonnumsv.o1o0g1o5o0.quest --qc Questioner --am src.nonnumsv.o1o0g1o7o0 --ac Answerer
Output:
quiz:eV0LI6Ep-AT=[@f,\}">d#:xn/|$t47WORZ.%rN&Kq3;1lQm]9s*5J`bCuG~!oDz8kgF<(+j_ivyc?w^hMP2HUBY'XS{a)
answer:['eV', '0', 'Ep-AT=[@f,\\}">d#:xn/|$t', '6', 'LI', '47', 'WORZ.%rN&Kq', '3', ';', '1', 'lQm]', '9', 's*', '5', 'J`bCuG~!oDz', '8',
'kgF<(+j_ivyc?w^hMP', '2', "HUBY'XS{a)"]
[Error] the string is different
> actual :eV0Ep-AT=[@f,\}">d#:xn/|$t6LI47WORZ.%rN&Kq3;1lQm]9s*5J`bCuG~!oDz8kgF<(+j_ivyc?w^hMP2HUBY'XS{a)
> expected:eV0LI6Ep-AT=[@f,\}">d#:xn/|$t47WORZ.%rN&Kq3;1lQm]9s*5J`bCuG~!oDz8kgF<(+j_ivyc?w^hMP2HUBY'XS{a)
「 いつも正解って言ったり、いつも間違いというようなプログラムでないことは 分かったが……」
「 テストしたいプログラムを100回、 わざと間違えるプログラムを100回、
ランダムな順序で実行して、
テストしたいプログラムの答えが正解の回数、わざと間違えるプログラムの答えが間違いの回数を加算して、
実行回数当たりの ちゃんと動いた回数の割合を出してくれだぜ」
「 じゃあ 100回ずつと言わず、 計2000回やって、小数点1桁の精度の百分率を出すかだぜ」
├── 📂 src
│ ├── 📂 nonnumsv
│ │ ├── 📂 o1o0g1o2o0
│ │ │ └── 📄 __init__.py
│ │ ├── 📂 o1o0g1o3o0
│ │ │ └── 📄 __init__.py
│ │ ├── 📂 o1o0g1o4o0
│ │ │ └── 📄 __init__.py
│ │ ├── 📂 o1o0g1o6o0
│ │ │ └── 📄 __init__.py
│ │ ├── 📂 o1o0g1o7o0
│ │ │ └── 📄 __init__.py
│ │ └── 📄 __init__.py
│ └── 📄 __init__.py
├── 📂 tests
│ ├── 📂 general
│ │ ├── 📂 o1o0g1o1o0
│ │ │ └── 📄 test.py
│ │ └── 📂 o1o0g1o8o0
👉 │ │ └── 📄 test.py
│ ├── 📂 nonnumsv
│ │ ├── 📂 o1o0g1o1o0
│ │ │ └── 📄 quest.py
│ │ └── 📂 o1o0g1o5o0
│ │ └── 📄 quest.py
│ └── 📄 __init__.py
├── 📄 .gitignore
├── 📄 LICENSE
└── 📄 README.md
"""
Example
-------
python -m tests.general.o1o0g1o8o0.test --qm tests.nonnumsv.o1o0g1o1o0.quest --qc Questioner --oam src.nonnumsv.o1o0g1o6o0 --oac Answerer --xam src.nonnumsv.o1o0g1o7o0 --xac Answerer --o 1000 --x 1000
"""
import argparse
import random
import math
from src.dimport import Dimport # Dynamic class import
# Command line arguments
ap = argparse.ArgumentParser()
ap.add_argument('--qm', help='questioner module')
ap.add_argument('--qc', help='questioner class')
ap.add_argument('--oam', help='correct answerer module')
ap.add_argument('--oac', help='correct answerer class')
ap.add_argument('--xam', help='incorrect answerer module')
ap.add_argument('--xac', help='incorrect answerer class')
ap.add_argument('--o', help='correct total count')
ap.add_argument('--x', help='incorrect total count')
args = ap.parse_args()
# Plan
# ----
Questioner = Dimport.load(args.qm, args.qc)
quest = Questioner()
CorrectAnswerer = Dimport.load(args.oam, args.oac)
IncorrectAnswerer = Dimport.load(args.xam, args.xac)
correct_total = int(args.o)
incorrect_total = int(args.x)
def check_correct(total):
"""正答数を数える"""
correct_count = 0
for i in range(0, total):
quiz = quest.make_quiz()
answer = CorrectAnswerer.to_answer(quiz)
err = quest.check(answer, quiz)
if err is None:
correct_count += 1
return correct_count
def check_incorrect(total):
"""誤答数を数える"""
incorrect_count = 0
for i in range(0, total):
quiz = quest.make_quiz()
answer = IncorrectAnswerer.to_answer(quiz)
err = quest.check(answer, quiz)
if not err is None:
incorrect_count += 1
return incorrect_count
# Do
# --
correct_rest = correct_total
incorrect_rest = incorrect_total
correct_count = 0
incorrect_count = 0
# Example
if 0 < correct_rest:
quiz = quest.make_quiz()
print(f"(1) quiz:{quiz}")
answer = CorrectAnswerer.to_answer(quiz)
print(f" answer:{answer}")
err = quest.check(answer, quiz)
if err is None:
correct_count += 1
else:
print(err)
correct_rest -= 1
# Example
if 0 < incorrect_rest:
quiz = quest.make_quiz()
print(f"(2) quiz:{quiz}")
answer = IncorrectAnswerer.to_answer(quiz)
print(f" answer:{answer}")
err = quest.check(answer, quiz)
if not err is None:
print(err)
incorrect_count += 1
incorrect_rest -= 1
print("...")
while 0 < correct_rest or 0 < incorrect_rest:
# 正答テスト
if 0 < correct_rest:
if incorrect_rest == 0:
times = correct_rest
else:
# 正答テストの残り件数の方が多かったら多めに行う
scale = math.ceil(correct_rest / incorrect_rest)
# ランダムに少し上乗せしてリズムを狂わす
scale += random.randint(0, 3)
times = min(scale, correct_rest)
correct_count += check_correct(times)
correct_rest -= times
# 誤答テスト
if 0 < incorrect_rest:
if correct_rest == 0:
times = incorrect_rest
else:
# 誤答テストの残り件数の方が多かったら多めに行う
scale = math.ceil(incorrect_rest / correct_rest)
# ランダムに少し上乗せしてリズムを狂わす
scale += random.randint(0, 3)
times = min(scale, incorrect_rest)
incorrect_count += check_incorrect(times)
incorrect_rest -= times
total_count = correct_total + incorrect_total
quality = (correct_count + incorrect_count) * 100 / total_count
# Check
# -----
print(f"""quality:{quality:.1f}% total:{total_count}""")
Input:
python -m tests.general.o1o0g1o8o0.test --qm tests.nonnumsv.o1o0g1o1o0.quest --qc Questioner --oam src.nonnumsv.o1o0g1o6o0 --oac Answerer --xam src.nonnumsv.o1o0g1o7o0 --xac Answerer --o 1000 --x 1000
Output:
(1) quiz:ABC123DEF456GHI
answer:['ABC', '123', 'DEF', '456', 'GHI']
(2) quiz:ABC123DEF456GHI
answer:['DEF', '123', 'ABC', '456', 'GHI']
[Error] the response is different. vec:['DEF', '123', 'ABC', '456', 'GHI']
...
quality:100.0% total:2000
おわり
Crieitは誰でも投稿できるサービスです。 是非記事の投稿をお願いします。どんな軽い内容でも投稿できます。
また、「こんな記事が読みたいけど見つからない!」という方は是非記事投稿リクエストボードへ!
こじんまりと作業ログやメモ、進捗を書き残しておきたい方はボード機能をご利用ください。
ボードとは?
コメント