Slide 1

Slide 1 text

Python で 自力シリアライズ すごい広島 .rb with Python[75] オンライン LTDD 2023-06 #1 @tsuda_ahr

Slide 2

Slide 2 text

お題 Python でシリアライズしたい。(ただしバイナリーで)

Slide 3

Slide 3 text

pickle を使えばいいよ、という情報をもらう

Slide 4

Slide 4 text

だめだった • バイナリーでシリアライズしたいだけでなく、型や配置などを制御したい。

Slide 5

Slide 5 text

どういうこと? • C言語の構造体/共用体や C# の FieldOffset みたいなことがしたい。 • (=メモリマッピングを制御したい) offset 0 0xf0 long lg (0x12345678abcdef0) int i1 (0x9abcdef0) 1 0xde 2 0xbc 3 0x9a 4 0x78 int i2 (0x12345678) 5 0x56 6 0x34 7 0x12 8 0x18 double d (0x400921fb54442d18) (3.141592653589793) 9 0x2d 10 0x44 11 0x54 12 0xfb char c (0x21fb) 13 0x21 14 0x09 byte b (0x09) 15 0x40

Slide 6

Slide 6 text

と、いうことで自作してみる

Slide 7

Slide 7 text

1. まずクラス定義 class Hoge: a:'I' = 0 # int16 b:'C10' = '' # char[10] c:'L' = 0 # int32

Slide 8

Slide 8 text

なにこれ? • アノテーションっていうの? • マニュアル(?)的には「Syntax for Variable Annotations」「型ヒント」 とか言われているっぽい機能の模様。 • クラスを vars で覗くと出てくる。

Slide 9

Slide 9 text

other-uses-of-annotations ? (他の使用方法?) • “アノテーションの好ましい使用方法として、型ヒンティングを明示的に推奨し ますが、型じゃないものをクォーテーションで括って指定してもエラーにはな らないよ”? https://peps.python.org/pep-0526/

Slide 10

Slide 10 text

2. インスタンスを作る hoge = Hoge() hoge.a = 1 hoge.b = '123ABC' hoge.c = 65538

Slide 11

Slide 11 text

3. アノテーションの取得と結果の初期化 annots = Hoge.__annotations__ result = bytes()

Slide 12

Slide 12 text

4. ライブラリの読み込み忘れてた(汗 import re import struct

Slide 13

Slide 13 text

struct? • “このモジュールは、Pythonの値とPythonバイトオブジェクトとして表現さ れるC構造体との間の変換を行います。コンパクトなフォーマットの文字列は、 Pythonの値との間で意図された変換を記述します。このモジュールの関数 とオブジェクトは、外部ソース(ファイルやネットワーク接続)とのデータ交換、 あるいはPythonアプリケーションとC層との間のデータ転送という、大きく 異なる2つの用途に使用することができます。” • C言語の struct とは異なる。その名を関したライブラリであることに注意。

Slide 14

Slide 14 text

4. メンバーのアノテーションと値を読み込み for var_name in annots.keys(): # 値の取得 value = getattr(hoge, var_name) # アノテーションの取得 annot = annots.get(var_name) # アノテーションのパース m = re.match(r'^(?P[A-Z])(?P[0-9]*)$', annot)

Slide 15

Slide 15 text

5. 変換(数値) # short 型(INT16)の値をエンコード if m.group('type') == 'I': result += struct.pack('

Slide 16

Slide 16 text

5. 変換(文字列) # sjis string の値をエンコード if m.group('type') == 'C': # 指定長を数値化して偶数値に切り上げ length = (int(m.group('length')) + 1) // 2 * 2 # エンコード a = value.encode('Shift-Jis') # 末尾の null padding a += bytes(length) result += a[0:length]

Slide 17

Slide 17 text

結果 >>> result b'¥x01¥x00123ABC¥x00¥x00¥x00¥x00¥x02¥x00¥x01¥x00' offset 0 0x01 INT16 a (1) 1 0x00 2 0x31 Char[10] b ('123ABC') 3 0x32 4 0x33 5 0x41 6 0x42 7 0x43 8 0x00 9 0x00 10 0x00 11 0x00 12 0x02 INT32 c (65538) 13 0x00 14 0x01 15 0x00

Slide 18

Slide 18 text

結論 • C# でいうところの属性はアノテーションで代替できる。 • ただし、変数に対してアノテーションは一つしか定義できなさそう。 (C# の属性は複数定義できる) • ユーザー定義のアノテーションを使ってしまうと型ヒントとしてつかえなさそ う。 • なんだけど、これで自作のシリアライザーは作れそう。

Slide 19

Slide 19 text

もし代案があれば? • もし、もっと良い方法とかあればご教示ください(ぇ

Slide 20

Slide 20 text

ご清聴ありがとうございました。