MermaidをWeb上で使えるMermaid Live Editorというサイトがある。
機密性を気にしなくていいような図なら、ここでササッと作ってもいいかもしれない。
個人的に、普段はPlantUMLをDockerで立てて使っている。
ただ、Gitのリビジョングラフの絵は、Mermaidのほうが見栄えがいい。
MermaidをWeb上で使えるMermaid Live Editorというサイトがある。
機密性を気にしなくていいような図なら、ここでササッと作ってもいいかもしれない。
個人的に、普段はPlantUMLをDockerで立てて使っている。
ただ、Gitのリビジョングラフの絵は、Mermaidのほうが見栄えがいい。
革製品ブランドのHERZで、文庫本サイズのブックカバーを買った。
注文してから1ヶ月強で到着。
普段、受注生産のものを買うことがないので、待っている間も結構楽しみだった。
今のところ、小説を読むときに使おうかなと思ってる。文庫本サイズのノートカバーとして使ってもいいかも。
革はかなり厚みがある感じ。 自分のことだから、雑に扱ってキズだらけになりそうな気がする。
革の質感がすごく良い。フワフワ柔らかい感触。匂いも好き。
表面には縫い目もロゴも無いものを選んだので、シンプルな見た目で長く使えそう。
Pythonで↓のようにEnumを分岐する処理を書いてみた。
from blood_types import BloodTypes def uranai(blood_type: BloodTypes): """血液型占い""" match blood_type: case BloodTypes.A: print("大吉") case BloodTypes.B: print("中吉") case BloodTypes.O: print("小吉")
PythonはJavaなどと違って、matchのルートに漏れがあってもコンパイルエラーにはしてくれない。
そのせいで、Enumに要素を追加する際に、実装漏れがないか結構気を使う。
(実際、↑のコードはAB型のルートが実装漏れしている)
そこで、以下のようにデフォルトのルートをNotImplementedErrorにしてみた。
from blood_types import BloodTypes def uranai(blood_type: BloodTypes): """血液型占い""" match blood_type: case BloodTypes.A: print("大吉") case BloodTypes.B: print("中吉") case BloodTypes.O: print("小吉") case _: raise NotImplementedError()
↑の修正に加えて、Enumの全パターンを通すようなユニットテストも用意する。
import unittest import blood_type_uranai from blood_types import BloodTypes class BloodTypeUranaiTest(unittest.TestCase): def test_uranai(self): # 全部の血液型で、エラーにならないことを確認 for blood_type in BloodTypes: blood_type_uranai.uranai(blood_type)
こういうユニットテストを用意しておけば、
Enumに要素が追加された場合に実装漏れしている処理があっても、テストがコケるのですぐに気がつくはず。
Python3.10からmatch文が追加されたそう。
今までは、Enumを処理するにも↓のようにif elif を繰り返して書いていたのが
from blood_types import BloodTypes def uranai(blood_type: BloodTypes): """血液型占い""" if blood_type == BloodTypes.A: print("大吉") elif blood_type == BloodTypes.B: print("中吉") elif blood_type == BloodTypes.O: print("小吉") elif blood_type == BloodTypes.AB: print("凶") else: raise NotImplementedError()
↓のようにmatch文できれいに書けるようになった。
from blood_types import BloodTypes def uranai(blood_type: BloodTypes): """血液型占い""" match blood_type: case BloodTypes.A: print("大吉") case BloodTypes.B: print("中吉") case BloodTypes.O: print("小吉") case BloodTypes.AB: print("凶") case _: raise NotImplementedError()
match文はEnum専用というわけではなく、他にも色々使えるらしい。
Pythonってswitch文ないの!?と驚いていたけど、これは嬉しい。
PythonでEnumを使おうとすると、各項目にvalueを設定する必要がある。
↓こんな感じ
from enum import Enum class BloodTypes(Enum): A = "a" B = "b" O = "o" AB = "ab"
JavaやC#から移ってきた人間には違和感があったけど、auto()関数を使えば、いちいち値を考えなくてもいいみたい。
from enum import Enum, auto class BloodTypes(Enum): A = auto() B = auto() O = auto() AB = auto()
値が不要な場合のほうが多そうだし、基本はauto()関数を使えばよさそう。
前回、generate_series関数でダミーデータを作る方法を書きました。
generate_series関数はちょっとしたデータの集計にも使えるので、今回はそれについて書きます。
例えば以下のようなテーブルがあったとします。
(何かしらのログが登録されるテーブルをイメージしています)
CREATE TABLE hoge_log ( created_at timestamp NOT NULL );
1日毎に何件のレコードがあるか調べたい場合、単にGROUP BYすればよいです。
SELECT date_trunc('DAY', created_at)::date AS log_date, count(*) FROM hoge_log GROUP BY log_date ORDER BY log_date ; /* log_date | count ------------+------- 2019-01-01 | 2 2019-01-02 | 1 2019-01-04 | 3 2019-01-05 | 1 (4 rows) */
ここでちょっと気になるのが
「件数が0件の場合、レコードが出力されない」
ということです。
上の例では、2019-01-03のレコードは出力されていません。
件数が0件の場合もレコードが出力されるようにしたい場合、generate_series関数が役に立ちます。
SQLは以下のようになります。
SELECT series.log_date, count(hoge_log.*) FROM ( SELECT date_trunc( 'DAY', generate_series( '2019-01-01 00:00'::timestamp, '2019-01-05 00:00'::timestamp, '1 day') )::date AS log_date ) series LEFT JOIN hoge_log ON (series.log_date = date_trunc('DAY', hoge_log.created_at)) GROUP BY series.log_date ORDER BY series.log_date ; /* log_date | count ------------+------- 2019-01-01 | 2 2019-01-02 | 1 2019-01-03 | 0 2019-01-04 | 3 2019-01-05 | 1 (5 rows) */
generate_series関数で日付の雛形を作ってあげて、それに対して実テーブルをLEFT JOINする形になっています。
count(hoge_log.*) のように、実テーブルのレコードだけをカウントしているのがポイントです。
generate_seriesという関数がダミーデータを作るのに便利で、度々使ってます。
例えば以下のような感じで、簡単に行を生成できます。
SELECT generate_series(1, 10, 1); /* 1から10まで1ずつ増やす */ /* generate_series ----------------- 1 2 3 4 5 6 7 8 9 10 (10 rows) */
これをFROM句に指定すれば、簡単にレコードを量産できます。
SELECT series AS id , 'hoge' AS value FROM generate_series(1, 10, 1) series ; /* id | value ----+------- 1 | hoge 2 | hoge 3 | hoge 4 | hoge 5 | hoge 6 | hoge 7 | hoge 8 | hoge 9 | hoge 10 | hoge (10 rows) */
増分を変えてみたり、、
SELECT series AS id , 'hoge' AS value FROM generate_series(1, 10, 2) series /* 2ずつ増やす */ ; /* id | value ----+------- 1 | hoge 3 | hoge 5 | hoge 7 | hoge 9 | hoge (5 rows) */
日付や時刻を引数に指定することもできます。
SELECT generate_series( '2019-01-01 00:00'::timestamp , '2019-01-07 00:00'::timestamp , '1 day'); /* generate_series --------------------- 2019-01-01 00:00:00 2019-01-02 00:00:00 2019-01-03 00:00:00 2019-01-04 00:00:00 2019-01-05 00:00:00 2019-01-06 00:00:00 2019-01-07 00:00:00 (7 rows) */
SELECT generate_series( '2019-01-01 00:00'::timestamp , '2019-01-01 05:00'::timestamp , '1 hour'); /* generate_series --------------------- 2019-01-01 00:00:00 2019-01-01 01:00:00 2019-01-01 02:00:00 2019-01-01 03:00:00 2019-01-01 04:00:00 2019-01-01 05:00:00 (6 rows) */
日付や時刻を扱うときは第3引数がinterval型になるので、 '1 days' なんて書きかたができます。 (interval型の記法は8.5. 日付/時刻データ型を参考に)
random関数とかを絡めれば、もっと自然なダミーデータを作れそう。
便利!