python2のディスクリプター使用方法

pythonのディスクリプタの使用方法についての覚書とテスト用コードを書いてみました。
#ディスクリプターとは？
python2.7で新しいスタイルのクラスを作成するとき（つまりobjectクラスを継承するとき）、objectクラスからデフォルトの辞書（`__dict__`アトリビュート）を継承します。

```python
>>> class A(object):
...     pass
...
>>> A.__dict__
dict_proxy({'__dict__': <attribute '__dict__' of 'A' objects>, '__module__': '__main__', '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None})
```
動的アトリビュートを追加してみると：

```python
>>> A.x = 5
>>> A.__dict__
dict_proxy({'__dict__': <attribute '__dict__' of 'A' objects>, 'x': 5, '__module__': '__main__', '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None})
```
辞書に`x`アトリビュートが追加されました。
[Raymond HettingerさんのHow toガイド](https://docs.python.org/2/howto/descriptor.html)を参考すると、
もしクラスにあるアトリビュート（仮に名前は`power`とします）が以下３つのメソッドのいずれを定義したら、このアトリビュートはディスクリプターと呼べます。

```python
power.__get__(self, obj, type=None)--> value
power.__set__(self, obj, value)--> value
power.__delete__(self, obj)--> None
```
ディスクリプターは参照`get`、値を与える`set`、削除`delete`する時、定義されたメソッドを使用します。感覚的に言うと、これらの動作を記述された（describe）アトリビュートがディスクリプター（descriptor）となります。
#ディスクリプターの動作
試しに３つのメソッド全部実装したディスクリプターを作って、クラスでテストしましょう。まずは作成：

```python
class B(object):
    class X(object):
        def __get__(self, obj, objtype):
            print 'loading descriptor'
            return self.val
        def __set__(self, obj, val):
            print 'updating descriptor'
            self.val = val
        def __delete__(self, obj):
            print 'descriptor is going'
            del self
    x = X()
```
これでアトリビュート`x`をもつクラス`B`を定義しました。加えて、`x`にはディスクリプタープロトコルに従いメソッドを定義しました。実際の動作をみてみましょう

```python
>>> b = B()
>>> b.x = 100
updating descriptor
>>> b.x
loading descriptor
100
>>> del b.x
descriptor is going
```
クラス`B`のインスタンス`b`を作り、インスタンスのディスクリプターが実装通りにゲット、セット、デリートできたのが確認できました。
*ちなみに*
`del`でインスタンスからディスクリプターを削除できたと思いきや

```python
>>> b.__dict__
{}
>>> b.x
loading descriptor
100
```
辞書アトリビュートから`x`を確認できませんが、`get`は普通に動作できます。それは`x`がクラスのアトリビュート（クラスの変数）だからです。故に、最初インスタンス`b.x`に値をセットする時直接クラスのアトリビュートをセットしました。削除する時もアトリビュートが定義されたクラスに渡して削除をさせます。
`__delete__`を更新して、再度インスタンスからディスクリプターを削除してみます。

```python 
# new __delete__ func in descriptor
def __delete__(self, obj):
    print 'descriptor is going' 
    print self
    print obj
    del self
# test again

>>> b = B()
>>> b.x = 100
updating descriptor
>>> del b.x
descriptor is going
# result in bpython interpretor
<__console__.X object at 0x10bb56310>
<__console__.B object at 0x10bd03390>

```
確認できた通り、インスタンスから`x`を削除しようが、実際に行われたのがクラスでのアトリビュートの削除です。

#データディスクリプター
ディスクリプターについてさらに細かい定義があります：
データディスクリプター：`__get__`と`__set__`を定義したディスクリプター
非データディスクリプター:`__get__`だけを定義したディスクリプター
アトリビュートの呼び出しの解決順は：
データディスクリプター > インスタンスの辞書 > 非データディスクリプター
これはどういうことでしょうか、随一テストしてみます。

##インスタンスの辞書 > 非データディスクリプター

まずクラスを用意します

```python
class B(object):
    def __delete__(self, obj):
        print "B is deleting"
        print self 
        print "obj", obj
    class X(object):
        def __init__(self, new_val):
            self.val = new_val
        def __get__(self, obj, objtype):
            print 'loading descriptor'
            return self.val
    x = X(100)
```
今回クラスのディスクリプター`X`は非データディスクリプターなので、初期化しないとアクセスするときエラーが起きます。なのでクラス内初期化しました。ちなみに`__delete__`を定義したらデータディスクリプターと見なされるそうです（`__get__`だけを定義して）。したがって`__delete__`を削除しました。

```python
>>> b = B()
>>> b.x
loading descriptor
100
>>> b.x = "Hi"
>>> b.__dict__
{'x': 'Hi'}
>>> b.x
'Hi'
>>> id(b.x)
4386348768
>>> id(B.x)
loading descriptor
140373344147024
```
テスト内容：
1. 非データディスクリプター`x`を持つクラス`B`のインスタンス`b`を作成
2. `b`が`x`を持っていること（アクセスできること）を確認します
3. `b`の`x`アトリビュートに新しい値をセットします。
4. `b`の辞書アトリビュートからインスタンスの変数の作成が確認できます
5. `b`から`x`アトリビュートを呼び出す。インスタンスの辞書にある`x`の値が返されます
6. 現在`b`がもつ`x`アトリビュートとクラス`B`が持つ`x`アトリビュートのidを比較して見ると、両者は違う変数とわかります。

以上でわかるように、クラスがもつアトリビュートが非データディスクリプターの時
- 参照をするだけなら問題なし
- クラスのインスタンスに同名アトリビュートをセットする時辞書アトリビュートに同名変数が作成されます
- 辞書アトリビュートに同名変数がある場合、辞書の変数が優先的に参照されます。

##データディスクリプター > インスタンスの辞書

まずクラスを用意します

```python
class B(object):
    def __delete__(self, obj):
        print "B is deleting"
        print self 
        print "obj", obj
    class X(object):
        def __init__(self, new_val):
            self.val = new_val
        def __get__(self, obj, objtype):
            print 'loading descriptor'
            return self.val
        def __set__(self, obj, val):
            print 'updating descriptor'
            self.val = val
    x = X(100)
```
前のテストと比べ、ディスクリプター`X`に`__set__`を追加し、データディスクリプターにしました。

```python
>>> b = B()
>>> b.x
loading descriptor
100
>>> b.x = "Hi"
updating descriptor
>>> b.__dict__
{}
>>> b.x
loading descriptor
'Hi'
>>> b.__dict__['x'] = "new value"
>>> b.__dict__
{'x': 'new value'}
>>> b.x
loading descriptor
'Hi'
```
テスト内容：
1. データディスクリプターを持つクラス`B`のインスタンス`b`を作成
2. `b`が`x`を持っていることを確認
3. `b`の`x`アトリビュートに新しい値をセットします、アトリビュートの`__set__`が呼び出されることを確認
4. `b`の辞書アトリビュートを表示させ、新しい変数が作成されていないことを確認
5. `b`から`x`アトリビュートを呼び出し、値の更新を確認
6. `b`の辞書に`x`という名前の変数を入れます
7. 再度`b`から`x`アトリビュートを呼び出します、辞書にある変数が参照されないことを確認

以上で確認できたのは、クラスのアトリビュートがデータディスクリプターの時、インスタンスから名前での参照も更新はディスクリプターで実行されます。インスタンスに同名変数があってもなくても影響しません。

#感想
ようやく参照優先順までのテストを完了しました。普段使う時そんなにそこまでインスタンス辞書とクラスのディスクリプターの参照優勢順位をきにすることはあまりないと思います。よくあるケースは上書きされたくないデータやメソッドなどを非データディスクリプターにすることでしょう。