2015年12月22日

__dict__は原則使わないこと

クラスの __dict__ に関する注意点を述べます。


クラスのアトリビュートには、通常の class.attribute というアクセス方法以外にも、class.__dict__['attribute'] というアクセス方法もあります。

>>> class Test(object):
...     def __init__(self):
...         self.test1 = 1
...
>>> t = Test()

# 通常のアクセス
>>> t.test1
1

# __dict__を介してのアクセス
>>> t.__dict__['test1']
1

# アトリビュートの追加も可能
>>> t.__dict__['test2'] = 2
>>> t.test2
2
__dict__ にはクラスの全アトリビュートが dict 形式で格納されています。dict のインターフェースでアトリビュートへアクセスできるというのは、かなり便利な機能です。特に、アトリビュートの追加・削除が多いクラスでは、getattr() / setattr() を使うよりも記述が簡単になることが多いです。


しかし! この __dict__ は原則として使うべきではない機能です。理由は2つあります。

  1. __slots__ を定義しているクラスでは使えない
  2. __dict__ は内部情報に直接アクセスし、クラスのインターフェースを無視する

1.の __slots__ についてはこちらを参照下さい。
外部定義のクラスを使う場合には、たとえ今は __slots__ が定義されていなくても、次のバージョンアップで __slots__ が定義されるかもしれません。このリスクを考えると、外部定義のクラスに対しては __dict__ を使えなくなるはずです。


2.についてですが、例えば以下のようなクラスがあったとします。

class FixInt(object):
    # 固定のint値を保持
    def __init__(self, fixed):
        self.__fixed = int(fixed)

    # getter
    @property
    def fixed(self):
        return self.__fixed
この FixIntクラスは、Read Only 属性のアトリビュート __fixed を持っています。このクラスの fixed へのアクセスについて、getattr() と __dict__ を比較します。
>>> f = FixInt(10)

# getattr()でfixedにアクセス
>>> getattr(f, 'fixed')
10

# __dict__でfixedにアクセス
>>> f.__dict__['fixed']
Traceback (most recent call last):
  File "<stdin>", line 1, in 
KeyError: 'fixed'
getattr() では 'fixed' にアクセスできたのに、__dict__ では 'fixed' にアクセスできませんでした。
これは、getattr() は定義されているクラスのインターフェース(即ち、クラス設計者の意図)に従っているのに対し、__dict__ は内部情報に直接アクセスしようとしている、ということを意味します。当然、getattr() の方が望ましいアクセス方法です。



そんな __dict__にも、一点大きな長所があります。それは、getattr() / setattr() よりも動作速度が速いということです。
よって、満足のいく動作速度が得られない場合には、__dict__ を使うのはありだと思います。
しかし、これは最終手段であり、上で述べた使うべきでない2つの理由を頭に入れた上で、それでも使いたいという場合にのみ使うべきと思います。

0 件のコメント:

コメントを投稿