概要
python には、基本的なコンテナとして
- list
- dict
- set
が用意されています。で、他の言語(特にコンパイル系の言語)
やってた人がよく言ってくるのが
「読み取り専用のコレクションみたいなのないの? IReadOnlyCollectionみたいな」
って質問です。python の場合、明確にlistに対してはコレっていうように
決まった型があるわけではないのですが、同じような効果を持つオブジェクトが
いたりします。
- list に対しては tuple
- dict に対しては types.MappingProxyType
- set に対しては frozenset
って感じです。
types.MappingProxyTypeだけ、ちょっと特殊ですね。
こいつは、動的ビューなイメージを持つクラスで、元データの変更は即座に反映するけど
データの追加などはサポートしていないっていう型です。
サンプル
以下、サンプルです。
""" 基本コンテナオブジェクトの読み取り専用モードオブジェクトについてのサンプルです。 """ import types from trypython.common.commoncls import SampleBase from trypython.common.commonfunc import pr, hr class Sample(SampleBase): def exec(self): ################################################################### # # 基本コンテナオブジェクトと読み取り専用オブジェクト # # Python の基本コンテナとして # * リスト (list) # * ディクショナリ (dict) # * セット (set) # が存在するが、それぞれ読み取り専用として扱えるオブジェクトが # 対で存在する。対応としては以下のようになる。 # # リスト # tuple に変更すると読み取り専用のリストとして使える # ディクショナリ # types.MappingProxyType オブジェクトが読み取り専用として使える # セット # frozenset が読み取り専用として使える # ################################################################## # # listとtuple # a_list = list(range(10)) pr('list', a_list) a_list.append(100) a_list.insert(0, 101) del a_list[1] pr('list is mutable', a_list) a_tuple = tuple(a_list) pr('tuple', a_tuple) try: # noinspection PyUnresolvedReferences a_tuple.append(102) except AttributeError as attr_ex: pr('tuple is immutable', attr_ex) try: # noinspection PyUnresolvedReferences del a_tuple[0] except TypeError as type_ex: pr('tuple is immutable2', type_ex) # tupleは生成時に元データをコピーして生成されるので # 元のデータに変更があっても影響はない a_list.clear() a_list.append(999) pr('tuple', a_tuple) hr() # # dictとtypes.MappingProxyType # a_dict = dict(hello=1, world=2) pr('dict', a_dict) a_dict['spam'] = 100 del a_dict['world'] pr('dict is mutable', a_dict) a_proxy = types.MappingProxyType(a_dict) try: # noinspection PyUnresolvedReferences a_proxy['hello'] = 200 except TypeError as type_ex: pr('MappingProxyType is immutable', type_ex) try: # noinspection PyUnresolvedReferences del a_proxy['hello'] except TypeError as type_ex: pr('MappingProxyType is immutable2', type_ex) # このオブジェクト自体が動的ビューなので # 元オブジェクトの変更は即座に反映される。 # (tupleやfrozensetの場合は元の値がコピーされているため元データ # 変更しても影響はない。) a_dict['world'] = 999 pr('proxy', a_proxy['world']) hr() # # setとfrozenset # a_set = set(a_tuple) pr('set', a_set) a_set.add(10) a_set.add(3) a_set.remove(2) pr('set is mutable', a_set) a_frozenset = frozenset(a_set) try: # noinspection PyUnresolvedReferences a_frozenset.add(4) except AttributeError as attr_ex: pr('frozenset is immutable', attr_ex) try: # noinspection PyUnresolvedReferences a_frozenset.remove(10) except AttributeError as attr_ex: pr('frozenset is immutable2', attr_ex) # frozensetは生成時に元データをコピーして生成されるので # 元のデータに変更があっても影響はない a_set.clear() a_set.add(999) pr('frozenset', a_frozenset) hr() def go(): obj = Sample() obj.exec() if __name__ == '__main__': go()
実行結果は以下のようになります。
list=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] list is mutable=[101, 1, 2, 3, 4, 5, 6, 7, 8, 9, 100] tuple=(101, 1, 2, 3, 4, 5, 6, 7, 8, 9, 100) tuple is immutable=AttributeError("'tuple' object has no attribute 'append'",) tuple is immutable2=TypeError("'tuple' object doesn't support item deletion",) tuple=(101, 1, 2, 3, 4, 5, 6, 7, 8, 9, 100) -------------------------------- dict={'hello': 1, 'world': 2} dict is mutable={'hello': 1, 'spam': 100} MappingProxyType is immutable=TypeError("'mappingproxy' object does not support item assignment",) MappingProxyType is immutable2=TypeError("'mappingproxy' object does not support item deletion",) proxy=999 -------------------------------- set={1, 2, 3, 4, 101, 5, 6, 7, 8, 9, 100} set is mutable={1, 3, 4, 101, 5, 6, 7, 8, 9, 100, 10} frozenset is immutable=AttributeError("'frozenset' object has no attribute 'add'",) frozenset is immutable2=AttributeError("'frozenset' object has no attribute 'remove'",) frozenset=frozenset({1, 3, 4, 101, 5, 6, 7, 8, 9, 100, 10}) --------------------------------
参考情報
Fluent Python ―Pythonicな思考とコーディング手法
- 作者: Luciano Ramalho,豊沢聡,桑井博之,梶原玲子
- 出版社/メーカー: オライリージャパン
- 発売日: 2017/10/07
- メディア: 単行本(ソフトカバー)
- この商品を含むブログを見る
types.MappingProxyTypeは、この本から知りました。
5. データ構造 — Python 3.6.3 ドキュメント
8.9. types — 動的な型生成と組み込み型に対する名前 — Python 3.6.3 ドキュメント
4. 組み込み型 — Python 3.6.3 ドキュメント
補足
後からここ見て知りましたが
dictの場合はnamedtupleでもいいかもしれないですね。
a_dict = dict(hello=1, world=2) MyFrozenDict = collections.namedtuple('MyFrozenDict', a_dict.keys()) a_frozendict = MyFrozenDict(**a_dict) print(a_frozendict.hello) a_frozendict.hello = 100 # raise AttributeError
まあ、アクセス方法がちょっと変わってしまうので、なんとも言えないですが。
過去の記事については、以下のページからご参照下さい。
- いろいろ備忘録日記まとめ
サンプルコードは、以下の場所で公開しています。
- いろいろ備忘録日記サンプルソース置き場