ちんぱんの雑記

チラシの裏

ビット演算の基本

ビット演算を知らないといっても過言ではないくらい忘れていた。

あれは実質知らなかったな…。

 

与えられた数値の桁数」が奇数か偶数か判定をするとき、次のコードで判定をしていた。

if len(str(num)) % 2 == 0:

 

別に上記のものでも動作するのでいいのだけれども、ビット演算を使うと次のようになる。

while num:
  digit_count += 1
  num //= 10
 
return (digit_count & 1) == 0

 

どうやらビット演算の方が処理速度が速く、メモリの使用量も少ないみたい。

 

この知見が活きる未来がまだ見えないけど、

趣味でオーバーエンジニアリングする時に積極的に活用していきたいなあ、と思った。

 

適切な場面でこの知識を引き出してこられるかは怪しいが楽しみは増えた。

 

ついつい len 関数でやりたくなりそうだけど、ビット演算を使った解法の方が好きだな。

 

str に変換するのがなんかダサいし、数値のまま判定できた方がかっこいいなって思う。

mock を使うとき、autospec を併用するといいかも!

autospec を使うことで、もともとのオブジェクトの挙動を模倣してくれます。

逆に、使わなければ模倣しないわけです。

 

なので、autospec を使わない場合、予期しない引数で呼ばれたり、

typo などによる存在しないメソッドの呼び出しがあったとき、エラーが発生しない可能性があるわけです。

 

例えば、次のようなクラスがあるとします。

class Calculator:
    def add(self, num1: int, num2: int) -> int:
        return num1 + num2

 

そして、テストはこんな感じ。

autospec を説明するためのコードであり、テストコードとしてはイケてないのは勘弁してほしい。

from my_test.my_test import Calculator
from unittest import mock


def test_add() -> None:
    with mock.patch.object(Calculator, "add", autospec=True):
        test_instance = Calculator()
        test_instance.add(1, 2)


def test_add2() -> None:
    with mock.patch.object(Calculator, "add"):
        test_instance = Calculator()
        test_instance.add(1, 2, 3)

 

test_add は呼び出し方に何ら問題はないので、当然成功してほしいです。

test_add2 では add に渡す引数が多く、使い方を誤っているので成功してほしくないわけです。

 

autospec を使っていないので Calculator クラスの本来の挙動を模倣してくれません。

なので、おかしなことをしていると検知できず、このテストは成功となってしまいます。

いけませんね。

 

なので、mock を使うときは autospec を積極的に使うべきだなあと思いました。

 

そもそも、静的解析を使っていると警告が出てくるので未然に防げるのですが…。

pytest のカバレッジ出力に必要な設定

カバレッジを HTML 形式で出せたらいいなと思ったので、必要な設定などをまとめておこうと思う。備忘録も兼ねて。

 

事前にインストールしておくものはこの2つだった。

pip install coverage --timeout=9999
pip install pytest-cov --timeout=9999

 

--timeout=9999 は通信環境によっては必要な人がいるかもしれない。

 

 

いざ、カバレッジを出力しながらテストを実行する場合のコマンドは次のとおりだった。

pytest --cov-report=html --cov=test2

--cov-report=html これはこのまま流用できる。

このオプションをつけておけば、カバレッジのHTMLを出力してくれる。

 

--cov=XXX これは XXX の部分の網羅率を出力するということになる。

ここは結構大事かもしれない。

 

例えば、test_target というパッケージの網羅率だけ関心がある場合は、次のようになる。

--cov=test_target

 

pytest --help にも書いてある説明は次のとおり。

Path or package name to measure during execution (multi-allowed). Use --cov= to not do any source filtering and record everything.

なので、パスかパッケージ名を指定すればいい。

 

--cov= だけを指定した場合、なぜかすべての記録をしなかったことは気になっている…。

使い方が悪いのか?

 

ルートディレクトリーで --cov=. のように末尾にドットを指定すればルートディレクトリー配下のすべてのモジュールの網羅率を出力するので、これでもいいのかもしれない。

 

さて、コマンドで実行する場合のことがわかったのはいいが、

実際にテストをする場合、コマンドをいちいち打たないだろうなと思うと、

設定ファイルを作成する方法を学んだ方が良いのかもしれないな。

VSCode でポチポチしたら対象の網羅率を出力するように設定できれば最善なのかな。

 

その確認はまた後日になるかな、今日はここまで。

アプリの自動更新に注意

Python コマンドを実行すると Microsoft Store が立ち上がって、想定どおりに使えなくなったって話。

 

使うためには python3.11 -m venv XXX みたいにバージョンを書かなければいけなくなっていた。

 

どうやら2日前に Python が更新されていたことに起因していると考えられる。

正しく解釈できているかは怪しいけど、Python の更新情報を見るとそれっぽいことが書いてあった。

 

環境変数に設定しているパスにある exe ファイルを直接実行してみるまでいまいちわからなかったんだよなあ。

python3.11.exe を起動したときに見慣れた画面になったから、それを指定しなければいけないんだなということがわかったのは解決への一歩だったな。

python.exe も python3.exe も更新日時が変わっていたのも気づけて良かった。

数値の初期化

0 や None 以外で数値の初期化する方法は -float("inf") もあるって話。

-float になっているのは負の数であることを意味していて、inf は infinity ってこと。

 

実務で使うかどうかは別だけど、そういう選択肢を持っておいて損はないと思う。

さして得もないかもしれないけど…。

 

例えば、整数の配列の3番目の最大値があればそれを返却、3番目の最大値が無ければ1番目の最大値を返却する関数があったとする。

実装の例は次のとおり。

def third_max(nums: List[int]) -> int:
    first_max: float = -float("inf")
    second_max: float = -float("inf")
    third_max: float = -float("inf")

    for num in nums:
        if num > first_max:
            third_max = second_max
            second_max = first_max
            first_max = num
        elif first_max > num > second_max:
            third_max = second_max
            second_max = num
        elif second_max > num > third_max:
            third_max = num

    if third_max == -float("inf"):
        return int(first_max)
    else:
        return int(third_max)

 

なんで 0 にしないかというと、3番目の最大値が負の整数だった場合におかしくなるし、意図せず影響しちゃうから。

でも -float("inf") ならそんなことは気にしなくても大丈夫。

 

None にしないのは None > int が成立しないから。

最初は None で初期化するのは名案だと思ったんだけどな…。

 

見直して思ったけど、変数の定義や最後の分岐は pythonic に書き換えられそうな気がした。

 

Docker Tutorial の記録3

これの続きとして書いていく。

Docker Tutorial の記録2 - ちんぱんの雑記

 

今までいろいろ動かしてきたけど、作ったボリュームってどこで確認できるんだ?と調べた結果、次のコマンドできるようだった。

$ docker volume ls

これと同様に network も確認できた。

$ docker network ls ですね。

 

どうやら、$ docker 確認したいもの ls で確認できる模様。

$ docker image ls でイメージの一覧も確認できたし。

 

 

$ docker run -it --network todo-app nicolaka/netshoot

-i は interactive の略で対話形式のプロンプトにしますよって話で、

-t は teletypewriter の略らしい。

-it の組み合わせでよく使われるみたい。

i だけだと標準入力は開かれているんだけど、利用者側のターミナルと接続されていない状態になるみたい。

その橋渡しの役割を担ってくれるのが -t ということか。

つまり、対話が不要な場合は -i だけでよくて、対話をしたい場合は -it を使うということか。

自分が使う未来は見えないけど、何かの設定を自動化したいとか、対話が不要な状況では -i だけで利用されるんだろうなあ。

対話不要って遊戯王の先行制圧みたいだな。厳密には対話拒否だが。

 

$ docker compose up -d で実行してみたらなんと便利なことでしょう。

複数のコンテナが同時に連携した状態で起動した。

そういうコマンドだから当然なんだけど…。

 

うーん、まだまだ理解していないんだろうなあという感覚はまだ拭い去れないな。

チュートリアルを何度かやり直したり、実務で使っているものを見たりして理解を徐々に深めていくことになりそうだなあ。

 

Docker Tutorial の記録はこれでおしまい。

Docker Tutorial の記録2

これの続きとして書いていく。

Docker Tutorial の記録1 - ちんぱんの雑記

 

$ docker run -dp 127.0.0.1:3000:3000 --mount type=volume,src=todo-db,target=/etc/todos getting-started

-dp は前回の復習となるが、 detach と publish の略で、

バックグラウンドでの動作とポートマッピングをしてもらうためのオプションですね。

 

$ docker run --help で確認した --mount の説明を転記するが「Attach a filesystem mount to the container」とある。

あえて説明を書くまでもないか…、オプションの文字どおりでマウントしますよって話。

 

$ docker run --mount で指定できる引数の一覧を確認しておきたいなあと思ったけど、

そんなコマンドはないらしいので、公式の情報を確認するしかないみたい。

うーん、なるほど、データベースとコンテナを紐づけるためには「ボリュームマウント」という選択肢が有効らしい。

 

 

そして、ホストマシン上のファイルシステムをコンテナと共有する場合は「バインドマウント」という選択肢が有効なのか。

 

ここで気づいたことだが、今のプロジェクトではボリュームマウントをしているようだな。

 

ボリュームマウントとバインドマウントをどのように使い分けするとよいのか想像できないが、今はそういうことができるんだということがわかれば十分か…。

 

Part7 以降で気になったことがあれば別の記事で書いていこうと思う。