公開日2023.01.11
最終更新日2023.01.06
もくじ
はじめに
Pythonを使用したPJ(プロジェクト)に関わってる中で、そこそこ量の構造化された情報を操作する場合がある。
多次元配列(list,tuple,dict)で対処することも可能であるが、静的型付け言語の構造体のように型補完しやすい状態で扱いたい。
Pythonでは3.5から型アノテーションが導入されており、
以下の条件でできないか、調査してみた。
- 多次元の構造で型補完ができるか
- 今回の構造体はほぼ、定数的な扱いなので、再代入不可にできるか
- 構造体を繰り返し処理できるか
使用したIDE、バージョン
- Visual Studio Code
- Python 3.9.6
結論 – NamedTupleを使用 –
typing moduleのNamedTupleを使用することで、
上記を全て満たす構造体を実現することができた。
NamedTupleとは名前付きタプルとのこと。
つまり再代入不可でかつパラメータを指定することもでき、
さらにこのモジュールは型アノテーションも可能である。
試行錯誤をいくつか施したので、
うまくいかなかったパターンも結論のあとに掲載を行う。
from datetime import date
from typing import NamedTuple
class Hand(NamedTuple):
name : str
class Arm(NamedTuple):
name : str
hand : Hand
class Foot(NamedTuple):
name : str
class Human(NamedTuple):
name : str
birth_date : date = date(2017,5,1) # フィールド設定時に代入することで初期値の設定可能
right_arm : Arm = Arm('伝説の右腕',Hand('伝説の右手'))
left_arm : Arm = Arm(name='黄金の左腕',hand=Hand('黄金の左手'))
right_foot : Foot = Foot('神が愛した右足')
left_foot : Foot = Foot(name='世界が泣いた左足')
if __name__ == '__main__':
legend_human = Human('伝説の人類')
print('--- index指定と名前指定で呼び出しできるか ---')
print('名前',legend_human[0], legend_human.name)
print('誕生日',legend_human[1], legend_human.birth_date)
print('右腕',legend_human[2][0], legend_human.right_arm.name)
print('左手',legend_human[3][1][0], legend_human.left_arm.hand.name)
print('--- 再代入するとエラー検知されるか ---')
try:
legend_human.name = '伝説の再代入'
print('失敗:再代入に成功しました')
except Exception as e:
print(e.__doc__)
print('--- 繰り返し処理ができるか ---')
for parts in legend_human:
print(parts)
補完も以下のようにlegend_human.right_armと階層化されていても可能である
そして、実行結果も想定した通り出力された。
% python3 named_tuple.py
--- index指定と名前指定で呼び出しできるか ---
名前 伝説の人類 伝説の人類
誕生日 2017-05-01 2017-05-01
右腕 伝説の右手 伝説の右手
左手 黄金の左手 黄金の左手
--- 再代入するとエラー検知されるか ---
Attribute not found.
--- 繰り返し処理ができるか ---
伝説の人類
2017-05-01
Arm(name='伝説の右手', hand=Hand(name='伝説の右手'))
Arm(name='黄金の左腕', hand=Hand(name='黄金の左手'))
Foot(name='神が愛した右足')
Foot(name='世界が泣いた左足')
NamedTupleの書き方
- NamedTupleを継承したclassを作成する
- フィールドにフィールド名と型をつける
from datetime import date
from typing import NamedTuple
class Human(NamedTuple):
id: int
name: str
birth_date: date = date(2017,5,1) # フィールド設定時の代入でデフォルト値の設定可能
失敗したパターン
今回の構造体のようなものが作れるかの取り組みは当初は
collectionsのnamedtupleで実装できるか模索していたが、
動作はするものの、型判定により補完が効かなかったので、断念した。
公式ドキュメント
from datetime import date
from collections import namedtuple
Hand = namedtuple('Hand',('name'))
Arm = namedtuple('Arm',('name','hand'))
Foot = namedtuple('Foot',('name'))
Human = namedtuple('Human',
('name','birth_date','right_arm','left_arm','right_foot','left_foot')
)
if __name__ == '__main__':
right_hand = Hand('伝説の右手')
left_hand = Hand('黄金の左手')
right_arm = Arm('伝説の右手',right_hand)
left_arm = Arm('黄金の左腕',left_hand)
right_foot = Foot('神が愛した右足')
left_foot = Foot('世界が泣いた左足')
legend_human = Human(
'伝説の人類',date(2017,5,1),right_arm,left_arm,right_foot,left_foot)
print('--- index指定と名前指定で呼び出しできるか ---')
print('名前',legend_human[0], legend_human.name)
print('誕生日',legend_human[1], legend_human.birth_date)
print('右腕',legend_human[2][0], legend_human.right_arm.name)
print('左手',legend_human[3][1][0], legend_human.left_arm.hand.name)
print('--- 再代入するとエラー検知されるか ---')
try:
legend_human.name = '伝説の再代入'
print('失敗:再代入に成功しました')
except Exception as e:
print(e.__doc__)
print('--- 繰り返し処理ができるか ---')
for parts in legend_human:
print(parts)
終わりに
調べてみたら案外typingモジュールのNamedTupleの情報が少なくて驚きました。
補完が効くのでコーディング中の情報量が多くて脳内記憶で対応できない時に重宝しています。
Pythonに関連するその他記事はこちら