LoginSignup
19
14

More than 5 years have passed since last update.

dict型のプロパティをread-onlyにするには

Posted at

作成したクラスに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の辞書として利用できます。

19
14
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
19
14