csvのheaderを気にしてOrderedDictで読む方法
問題
例えば以下のようなcsvがあるときに、読み込んだ後にcsvのheaderの順序を保持してほしいという場合がある。
"age" "name" "20" "foo"
カジュアルに以下の様なコードで読んだ場合に順序は不定。
import csv import sys r = csv.DictReader(sys.stdin, delimiter="\t") print(list(r))
順序は不定。
[{'name': 'foo', 'age': '20'}] [{'name': 'foo', 'age': '20'}] [{'age': '20', 'name': 'foo'}]
対応方法
csv.DictReaderを継承するしか無いの?
例えば以下の様な感じにする。dict_factoryみたいな形で引数になっているとありがたいのだけれど。
import csv import sys from collections import OrderedDict class OrderedDictReader(csv.DictReader): def __next__(self): if self.line_num == 0: # Used only for its side effect. self.fieldnames row = next(self.reader) self.line_num = self.reader.line_num # unlike the basic reader, we prefer not to return blanks, # because we will typically wind up with a dict full of None # values while row == []: row = next(self.reader) d = OrderedDict(zip(self.fieldnames, row)) lf = len(self.fieldnames) lr = len(row) if lf < lr: d[self.restkey] = row[lf:] elif lf > lr: for key in self.fieldnames[lr:]: d[key] = self.restval return d r = OrderedDictReader(sys.stdin, delimiter="\t") print(list(r))
今度は大丈夫。
[OrderedDict([('age', '20'), ('name', 'foo')])] [OrderedDict([('age', '20'), ('name', 'foo')])] [OrderedDict([('age', '20'), ('name', 'foo')])]
logging入門
長すぎにならない程度に使い方をまとめてみる。
loggingの使い方
ライブラリの利用者
既に存在するアプリを実行するファイルの場合
if __name__ == "__main__": import logging logging.basicConfig(level=logging.DEBUG) # or INFO or WARNING or ERROR run()
個人的には時間もみたいので以下の様にしている。
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
設定可能な属性について詳しくはここ
ライブラリの作成者
ライブラリ内の話。基本的にはモジュール毎に __name__
でロガーを作る。アプリを作成する際にはご自由に。
import logging logger = logging.getLogger(__name__) # おもに問題を診断するときにのみ関心があるような、詳細な情報。 logger.debug("hmm") # 想定された通りのことが起こったことの確認。 logger.info("hmm") # 想定外のことが起こった、または問題が近く起こりそうである (例えば、'disk space low') ことの表示。 logger.warning("hmm") # より重大な問題により、ソフトウェアがある機能を実行できないこと。 logger.error("hmm") # プログラム自体が実行を続けられないことを表す、重大なエラー。 logger.critical("hmm")
最悪、debug
,info
,error
だけ使えば良い。
例外発生時のtracebackを出力したい場合
stack traceもログに出力したい場合には exc_info=True
をつける
try: foo() except: logger.warning("hmm", exc_info=True)
うるさいloggerを黙らせる
ロガーの名前が foo.bar.boo
の場合
logging.getLogger("foo.bar.boo").setLevel(logging.CRITICAL)
(sentryで適切にaggregationしたい場合)
loggerに渡す文字列をaggregation用のidとして利用できるようにする。具体的にはformat文字列などを利用して文字列を生成しない。
# ok logger.info("name: %s", name) # ng logger.info("name: {}".format(name))
後者は文字列フォーマットの適用結果がsentryに送られてしまうため。適切に発生したエラーをaggregateできない。