Python:全角文字でもPython書式を揃えたい
[ できるかな Python ] のコーナー
ここでは実用になるかはさておき、Pythonで出来たら楽しいって事をやってみるだけのコーナーです。
とうとう、禁断のクラスを作ることに手を染めてしまった・・・
strをサブクラス化してみるという禁断の手に!!
だって、__format__()とかオーバーライドしてみたいじゃん!
で、割とうまく言ったのだが!!
内部で使った正規表現が。。。いまいち自信ないね(笑)
まあ、動いてるっぽいよ未来の俺!
こんなに大袈裟なクラスがいらないなら、ワンライナーで書ける前回版をオススメするぞ私!
で、今回のレシピはこれ!
<材料>
- str型
- east_asian_width()
- 特殊メソッド__format__()
- 特殊メソッド__add__()
- 特殊メソッド__radd__()
<作り方のポイント>
- __format__()メソッドで全角文字の時に対応する
- __add__()および__radd__()をオーバーライドして文字列の結合に対応する
- east_asian_width()で全角文字を判断する
- formatを正規表現を使って上手に料理する ※これは保証できない(笑)
stringw.pyfrom __future__ import annotations # 自分自身のクラスを型注釈できるように(必ずソースの先頭) from unicodedata import east_asian_width from functools import reduce from typing import TypeVar, Generic, Callable import re class StringW(str): @property def length(self) -> int: return len(self) @property def width(self) -> int: # Japanese wide-char size is calculated as 2 bytes. return reduce(lambda a, c: a + 1 + (east_asian_width(c) in "FWA"), self, 0) @property def diff(self) -> int: return self.width - self.length def __add__(self, other: StringW) -> StringW: return StringW(super().__add__(other)) def __radd__(self, other: StringW) -> StringW: return StringW(other.__add__(self)) def __format__(self, format: str) -> str: formatreg = r"((?:\S?[<>=^])?[-+ ]?z?#?0?)(\d*)([_,]?\.?\d*\w?)" m = re.match(formatreg, format) def convert(m): if m[2]: width = int(m[2]) - self.diff if not width < 0: return f"{m[1]}{str(width)}{m[3]}" return m[0] newformat = re.sub(formatreg, convert, format) return super().__format__(newformat)
使い方は、文字列をStringW()に渡して、オブジェクトにするだけ!!
あ〜ら不思議!文字列が崩れることなく、表示されちゃう!!
しかも!Pythonのフォーマットも使えちゃう!!
<例>def example(): member: list[dict[str, str | int | float]] = [ { "user name":"あいうえ", "weight":90, "height":177 }, { "user name":"Super 真理雄", "weight":1250, "height":188 }, { "user name":"Ninja", "weight":70, "height":160 }, { "user name":"Mr.エックス", "weight":66, "height":167 }, ] mr = StringW("こんにちは、Mr.x") mr += "!!" print(mr) print(f"length: {mr.length} width: {mr.width}\n") for m in member: name = StringW(m['user name']) print(f"{name:_<13s} : {m['weight']:>8,.1f}Kg : {m['height']:>5}cm") if __name__ == "__main__": example()
<表示結果>こんにちは、Mr.x!! length: 12 width: 18 あいうえ_____ : 90.0Kg : 177cm Super 真理雄_ : 1,250.0Kg : 188cm Ninja________ : 70.0Kg : 160cm Mr.エックス__ : 66.0Kg : 167cm
ちなみに、プロパティの length はlen()関数と同じ文字数で、
width は全角文字を2とした幅を表していますぞ!
めでたしめでたし!
おお!今日は『ひな祭り』ではないですか!!
めでたい!めでたい!
コメント
コメントを投稿