作成したクラスにdict
型のプロパティがある場合、プロパティの要素をユーザーに勝手に変更されたくない場合に、MappingProxyTypeを用いてプロパティの要素の変更をできないようにする方法を説明します。
例えば次のようなコードを想定しています。
class Foo:
def __init__(self):
self._bar = {'a': 1, 'b': 2}
@property
def bar(self):
return self._bar
def add_item(self, key, value):
# ユーザーにはこのメソッドを通してのみ要素を追加してほしい
self._bar[key] = value
このコードでは、Fooクラスのプロパティbar
にはsetter
が定義されいないので、ユーザーはbar
を別のオブジェクトに変更することはできません。次のように、bar
に別の値をセットしようとしてもエラーになります。
>>> foo = Foo()
>>> foo.bar
{'b': 2, 'a': 1}
>>> foo.bar = 1
Traceback (most recent call last):
File "<pyshell#15>", line 1, in <module>
foo.bar = 1
AttributeError: can't set attribute
ですが、foo.bar
自体はdictなので要素の追加ができてしまいます。
>>> foo.bar['c'] = 3
>>> foo.bar
{'b': 2, 'a': 1, 'c': 3}
このようなクラスの作り手の意図しない変更を防ぐために、Python Ver 3.3 以降ではtypes
ライブラリのMappingProxyType
型を使う手があります。MappingProxyTypeのコンストラクタを用いて、返す辞書をmapping proxyオブジェクトにして返すとユーザーは返されたオブジェクトを変更できなくなります。 先ほどサンプルを変更するとこうなります。
from types import MappingProxyType
class Foo:
def __init__(self):
self._bar = {'a': 1, 'b': 2}
@property
def bar(self):
return MappingProxyType(self._bar)
def add_item(self, key, value):
self._bar[key] = value
次のようにユーザがbarに要素を追加しようとしても、エラーとなります。
>>> foo = Foo()
>>> foo.bar
mappingproxy({'a': 1, 'b': 2})
>>> foo.bar['c'] = 3 # 要素を追加しようとしてもエラーとなる。
Traceback (most recent call last):
File "<pyshell#29>", line 1, in <module>
foo.bar['c'] = 3
TypeError: 'mappingproxy' object does not support item assignment
>>> foo.add_item('c', 3) # ユーザーはメソッドを使って要素を追加する。
>>> foo.bar
mappingproxy({'a': 1, 'c': 3, 'b': 2})
mappingproxy
は、要素の取得はdict
と同じようにできるため、read-only
の辞書として利用できます。