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とした幅を表していますぞ!
めでたしめでたし!
おお!今日は『ひな祭り』ではないですか!!
めでたい!めでたい!
コメント
コメントを投稿