Python の attrs で validation をかける
以前 Qrunch に投稿してた記事を持ってきました。
Python の attrs が便利すぎて、データ保持用のクラスを作る時なんかは専らコレばっかり使っています。
そんな attrs で入力されるデータに validation をかける方法です。
attrs で入力データをチェック
例えば Pose
というクラスに対して、位置を表す position
は長さ 3 の配列、向きを表す orientation
は長さ 4 の配列(クォータニオン) であることをチェックする場合は以下のように書くことができます。
from attr import attrs, attrib @attrs class Pose: position = attrib() orientation = attrib() @position.validator def check_position(self, attribute, value): if len(value) != 3: raise ValueError("position must be a list with size=3") @orientation.validator def check_orientation(self, attribute, value): if len(value) != 4: raise ValueError("orientation must be a list with size=4")
正しい値を代入した場合
>>> Pose(position=[0, 1, 2], orientation=[0, 0, 0, 1]) Pose(position=[0, 1, 2], orientation=[0, 0, 0, 1])
不正な値を代入した場合
>>> Pose(position=[0, 1], orientation=[0, 0, 0, 0, 1]) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<attrs generated init d25cdc317f9cca07dcc37d3210f62862e6055553>", line 5, in __init__ File "<stdin>", line 8, in check_position ValueError: position must be a list with size=3
普通の class だと __init__
が肥大化したりしがちですが、これだとスッキリ書けますね!
関連サイト
周波数からC3やF4などのノート名に変換する
以前 Qrunch に投稿してた記事を持ってきました。
Python 3 で周波数からノート名に変換
A4 は 440Hz、 C3 は 130.8Hz のようにノート毎に周波数が決まっていますが、周波数からノートに変換するコードを Python 3 で書いてみました。
import math SCALE_LIST = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"] C0_FREQUENCY = 16.35 def frequency_to_note(freq): n = round(math.log2(freq / C0_FREQUENCY) * 12) return SCALE_LIST[n % 12] + str(n // 12)
実行するとこんな感じ。ノートの音程ぴったりじゃない場合は四捨五入で丸めています。
>>> frequency_to_note(55) 'A1' >>> frequency_to_note(440) 'A4' >>> frequency_to_note(130) 'C3' >>> frequency_to_note(185) 'F#3'
参考URL
Pythonでファイルに書き出さずにzip圧縮
この記事では、Python でファイルを生成せずに zip 圧縮したデータを生成する方法を紹介します。
zip 圧縮したデータを Python 内からアップロードしたい場合なんかに、ファイルに書き出して後から削除する手間を避けたかったり、FaaS などそもそも ReadOnly のファイルシステムで使ったりすることを想定しています。
動作環境
- Python 3.7
動かし方
Python の zipfile ライブラリと、 io.BytesIO を用いて実現します。
# zip 圧縮したデータをファイルに書き出す代わりに BytesIO のストリームを作成 zip_stream = io.BytesIO() # ファイルに書き出す代わりに zip_stream に zip 圧縮したデータを出力 with zipfile.ZipFile(zip_stream, 'w', compression=zipfile.ZIP_DEFLATED) as new_zip: # foo.txt というファイル名で abcdef という文字列をzipファイルに追加 new_zip.writestr("foo.txt", "abcdef") # ローカルの foo.png を zip ファイルに追加 new_zip.write("foo.png") # 生成された zip 圧縮データを出力 # 実際には print の代わりに HTTP の POST とか、S3 へのアップロードとかに使うイメージ print(zip_stream.getvalue())
参考URL
matplotlib でプロット上の点をドラッグする例
Python の matplotlib 上で、マーカーをドラッグ可能にするプログラムの参考例です。
動作環境
- python 2.7
- matplotlib 1.5.3
実装例
使い方
- プロット上で左クリックで点を追加
- プロット上の点をドラッグで移動
- プロット上の点を右クリックで削除
解説
そんなに難しいことはしておらず、matplotlib の canvas に対してマウスイベントを拾って、適切な処理を行うだけです。
- マウスのクリックイベント時のコールバック
_on_click
で新しい点の追加、もしくは既存の点の選択・削除処理 - マウス移動イベント時のコールバック
_on_motion
で選択中の点がある場合は移動処理 - マウスリリースイベント時のコールバック
_on_release
で選択中の点がある場合は確定処理
ちょっとずるいのは、点のX, Y座標を整数に絞ることで、点の管理を簡略化しているところかも。。。
もっと良い実装があれば PR くださーい!
参考URL
python の multiprocecssing.Pool.map で複数の引数を持つ関数を扱う
python の multiprocessing を用いてマルチスレッド処理を行う際に、複数の引数を持つ関数を扱うときの Tips です。
動作環境
- Python 2.7
方法
やり方は簡単で、目的となる関数をラップするだけです。
# -*- coding: utf-8 -*- from multiprocessing import Pool def calc(a, b, c): result = a + b * c print("%d + %d * %d = %d" % (a, b, c, result)) return result def wrap_calc(num): return calc(*num) def main(): pool = Pool(processes=4) args = [(1, 2, 3), (2, 3, 4), (3, 4, 5), (4, 5, 6)] print("result = %s" % pool.map(wrap_calc, args)) if __name__ == '__main__': main()
結果
$ python pool.py 1 + 2 * 3 = 7 2 + 3 * 4 = 14 4 + 5 * 6 = 34 3 + 4 * 5 = 23 result = [7, 14, 23, 34]
上記のように、wrap_calc
メソッドにリスト型で引数を渡し calc
メソッドに展開してあげれば解決します。