自作debug関数(python)
投稿者:kakip.icon
自作のdebug用関数を一応置いておきます。いろいろバグがあります。今度からC++を使う予定なので直すかどうかは未定です。読みにくいコード注意。下にわかりにくい使い方説明があります。github copilotくんの力を借りつつ結構頑張った
これをdebug.pyとして使いたいpythonファイルと同じフォルダに置く
code:python
from sys import stdout
from collections import defaultdict
from sortedcontainers import SortedSet
white_list = defaultdict(lambda: -1)
white_list |= {int: 0, float: 0, bool: 0, type(None): 0, str: 1, list: 1, tuple: 1, dict: 1, set: 1}
global_vars = {}
id_to_name = defaultdict(list)
def set_globals(globals_dict: dict) -> None:
global global_vars
global_vars = globals_dict
def set_id_to_name() -> None:
global id_to_name
id_to_name = defaultdict(list)
for key, value in global_vars.items():
if key.count("__") < 2:
def obj_to_dict(arg) -> dict:
result = {}
if isinstance(arg, str) and arg.count('=') == 1 and 'object' not in arg: # 例: arg = 'a, b, c = 1, 2, 3' names, values = arg.split('=')
names = ['result+ name.strip() + '"' for name in names1:-1.split(',')] try:
exec(','.join(names) + ' = ' + values) # 例: exec('result"a", result"b", result"c" = 1, 2, 3') except SyntaxError:
result = {'error': arg}
# 例: result = {"a": 1, "b": 2, "c": 3}
else:
result = {'|'.join(id_to_name.get(id(arg), 'no name')): arg} # 例: result = {'var_name': value_0, 'same_id_var_name1/same_id_var_name2': value_1_2, 'no name': value_3}
return result
def merge_ifbn(args: dict) -> dict:
dic = {}
for name, value in args.items():
for name in dic.keys():
return args | ({'ifbn': dic} if dic else {})
def sort(args: list[liststr]) -> list[liststr]: # argsを行数(要素数)の降順にソートする
def key(value: liststr) -> int: if 'ifbn' in value0: # margeされたint, float, boolのdictの場合 return 0
elif type(value0) == str and '\n' in value0: # 改行を含む文字列の場合 return value0.count('\n') + 1 return len(value)
return list(sorted(args, key=key, reverse=True))
def table(name: str, value, min_var_width: int, min_value_width: int, max_var_width: int, max_value_width: int) -> liststr: def calc_width(longest_width: int, limit1: int, limit2: int) -> int:
def adjust(obj, width: int, fill: str = ' ') -> str:
string = str(obj)
if len(string) > width:
else:
return string.rjust(width, fill)
def adjust_var(obj, var_width: int) -> str:
return f"+{adjust(' ' + str(obj), 1 + (len(name) if var_width == -1 else var_width), '-')}"
def adjust_var_value(var: str, value, var_l1: int, var_l2: int, value_l1: int, value_l2: int) -> str:
return f"{adjust_var(var, calc_width(len(var), var_l1, var_l2))} = {adjust(str(value), calc_width(len(str(value)), value_l1, value_l2))}"
def adjust_iterable(iterable, value_width: int) -> str:
def adjust_var_iterable(var: str, iterable, var_l1: int, var_l2: int, value_l1: int, value_l2: int) -> str:
return f"{adjust_var(var, calc_width(len(var), var_l1, var_l2))} {adjust_iterable(iterable, calc_width(max(map(len, map(str, value))), value_l1, value_l2))}"
def adjust_var_type(var, value, var_width: int) -> str:
return f"{adjust_var(var, var_width)} : {type(value).__name__}"
def adjust_table_head(var_width: int, value_width: int, max_len: int) -> str:
def adjust_table_line(key, values, var_width: int, value_width: int) -> str:
return f"| {adjust(key, var_width)} {adjust_iterable(values, value_width)}"
def change_type(arg):
if type(arg) in [map, type(reversed([]))]:
return list(arg)
return set(arg)
try:
return t(arg)
except (TypeError, ValueError):
pass
return arg
opening_brackets = {list: '[', tuple: '(', set: '{'}
closing_brackets = {list: ']', tuple: ')', set: '}'}
result = []
value = change_type(value)
result.append(adjust_var_value(name, value, min_var_width, max_var_width, min_value_width, max_value_width))
elif type(value) == dict:
keys = list(value.keys())
values = list(map(change_type, value.values()))
if not value: # 空dictの場合
result.append(" = {}")
elif all(type(v) in list, tuple, set for v in values): # dictのvalueがすべてlist, tuple, setの場合 longest_var_width = max(max(map(len, map(str, keys))), len(name))
longest_value_width = max(max(map(len, map(str, v))) if v else 0 for v in values)
var_width = calc_width(longest_var_width, min_var_width, max_var_width)
value_width = calc_width(longest_value_width, min_value_width, max_value_width)
result.append(adjust_var_type(name, value, var_width))
result.append(adjust_table_head(var_width, value_width, max(len(v) for v in values)))
for k, v in value.items():
result.append(adjust_table_line(k, v, var_width, value_width))
else:
longest_var_width = max(len("values"), len(name))
longest_value_width = max(max(map(len, map(str, keys))), max(map(len, map(str, values))))
var_width = calc_width(longest_var_width, min_var_width, max_var_width)
value_width = calc_width(longest_value_width, min_value_width, max_value_width)
result.append(adjust_var_type(name, value, var_width))
result.append(adjust_table_line("keys", keys, var_width, value_width))
result.append(adjust_table_line("values", values, var_width, value_width))
elif type(value) == str:
if '\n' in value:
result.append(adjust_var_value(name, value, min_var_width, max_var_width, min_value_width, 10 ** 10))
value = type(value)(map(change_type, value))
if not value:
result.append(adjust_var_value(name, value, min_var_width, max_var_width, min_value_width, max_value_width))
elif all(type(v) == str and len(v) == 1 for v in value):
result.append(
f"{adjust_var(name, calc_width(len(name), min_var_width, max_var_width))} = {opening_bracketstype(value)} *'{''.join(value)}' {closing_bracketstype(value)}") longest_var_width = max(max(len(str(len(value))), min_var_width), len(name))
longest_value_width = max(max(map(len, map(str, v))) if v else 0 for v in value)
var_width = calc_width(longest_var_width, min_var_width, max_var_width)
value_width = calc_width(longest_value_width, min_value_width, max_value_width)
result.append(adjust_var_type(name, value, var_width))
result.append(adjust_table_head(var_width, value_width, max(len(v) for v in value)))
for i, v in enumerate(value):
result.append(adjust_table_line(i, v, var_width, value_width))
else:
result.append(
adjust_var_iterable(name, value, min_var_width, max_var_width, min_value_width, max_value_width))
else:
result.append("table error")
return result
# debug 特殊オブジェクト zip reverse range map dict 1line repr inf auto range list
def debug(*args, sep=' ', end='\n', file=stdout, flush=False, normal=False, var_dict=None, one_line=False,
min_var_width=15, min_value_width=6, max_var_width=30, max_value_width=15, **kwargs) -> None:
if normal:
return print(*args, sep=sep, end=end, file=file, flush=flush)
if var_dict is None:
var_dict = global_vars
set_id_to_name()
vars_dict = {}
if args == ():
vars_dict = {key: value for key, value in var_dict.items() if
type(value) in white_list and (key + str(type(value))).count("__") < 2 and key != 'global_vars'}
else:
for arg in args:
vars_dict.update(obj_to_dict(arg))
vars_dict |= kwargs
if one_line:
for name, value in vars_dict.items():
print(f"{name: >{min_var_width}} = {str(value): <{min_value_width}} ", end="")
print()
return
vars_dict = merge_ifbn(vars_dict)
result = []
for name, value in vars_dict.items():
result.append(table(name, value, min_var_width, min_value_width, max_var_width, max_value_width))
result = sort(result)
return print(*result, sep='\n' if sep == ' ' else sep, end=end, file=file, flush=flush)
使い方
暇があったら説明追加します
注意:この説明を書いたときと仕様が変わっている可能性があります
code:python
if __name__ == '__main__':
# # 他のファイルからdebug.pyをimportする場合、普通にfrom debug import debugしてもいいが、
# # 以下のようにすると提出時にdebug()をコメントアウトする必要がなくなる
# def debug(*args, **kwargs):
# pass
# if "Main.py" not in __file__: # ファイル名の末尾をMain.pyにしないように注意
# from debug import debug, set_globals
# set_globals(globals())
# # ↓1行バージョン 場所をとらない
# if (debug := lambda *args, **kwargs: None) and "Main.py" not in __file__: from debug import debug, set_globals; set_globals(globals())
set_globals(globals()) # argsに引数を渡さない場合は必要
# メモ:コメントすること:入力、出力、引数、並び替え、幅の自動調整などの説明
# 使い方
# 表示したい変数の渡し方
# 1. debug(var1, var2, ...)
test_int = 123
test_float = 3.14
debug(test_int, test_float) # 引数のみ表示
"""
+------- ifbn : dict
"""
# 2. debug(f"{var1, var2, ...=}")
debug(f"{test_int, test_float=}") # f文字内の各変数のみ表示
# 3. debug() 一部のグローバル変数のみ表示される
debug()
from collections import defaultdict
dd = defaultdict(int, {1: 2, 3: 4})
# 4. debug(add_var_name1=value1, add_var_name2=value2, ...)
debug(d=dict(dd), ddk=dd.keys()) # グローバル変数に加えて任意の値を追加で表示
# 表示形式
# int, float, bool, Noneはifbnにまとめられる
# 説明が力尽きた
i = 123
f = 3.14
b = True
n = None
s = 'string'
sn = '1\n12\n123'
d = {'i': i, 'f': f, 'b': b, 'n': n, 's': s, 'l': l}
dl = {1: [], 'very very long second key': {1}, (3,): (1, 2)}
ll = [[], {1}, (1, 2)]
long_value_list = 1, 22, 333], [444, 55555, 666666, 7777777
dk = d.keys()
debug(min_var_width=15, max_var_width=30, min_value_width=4, max_value_width=14)
"""出力結果
+------- list_str : list
| | 0
+------------- sn = '''\
| 1
| 12
| 123'''
+ long_value_list : list
| | 0 1 2 3
+-------------- d : dict
+------------- dl : dict
+-------------- s = string
+ very_very_long_var_name_list [ 123 3.14 True None ['s', 't', 'r~ 1, 2, 333333~ +------------- ll [ {} {1} 1, 2 ] +----------- ifbn : dict
"""
追加したいこと
引数:inf 絶対値がinf以上の数値を"inf"に置換
引数:ran list(range(ran))を追加で表示
引数:??? 小数を指定桁数で四捨五入
引数:compact_dict dictを1行で表示するオプション
修正したいこと
defaultdictが弾かれる
1部の「型」でエラーがでる
list[空リスト]とかで要素が{}に変わる
white_listとか絶対いらない
コードの一貫性の問題:typing.Any, isinstance