

メタクラスを使用した例としてJavaScriptのprototypeをPythonで実装して見た例があります。  
このコードでは`Prototype`Classの`__getattr__`を定義して(1)、`Prototypeのインスタンス.prototype`が呼ばれた時にclass属性の`prototype`を見にいくようになっています。

(`if name == 'prototype':`のところ)

そこで気になったのですが(2)の`getattr`の呼び出しには`return`がついていません。それなのになぜ、`cls.prototype`が返るのでしょうか?  
`return getattr(self.__class__, name)`としなくて良いのは何故なのでしょうか?

[code]

    #!/usr/bin/env python
    ## -*- coding: utf-8 -*-
    
    class PrototypeStore(dict):
        """ x.prototype.XXXの値を保存するためのクラス """
        def __setattr__(self, name, value):
            self[name] = value
    
        def __getattr__(self, name):
            return self[name]
    
    class PrototypeMeta(type):
        """ Prototypeメタクラス(クラス生成時に呼ばれる) """
        def __new__(metacls, cls_name, bases, attrs):
            cls = type.__new__(metacls, cls_name, bases, attrs)
            cls.prototype = PrototypeStore()
            return cls
    
    class Prototype(object):
        __metaclass__ = PrototypeMeta
    
        def __getattr__(self, name): #(1)
            if name == 'prototype':
                getattr(self.__class__, name) #(2)
            else:
                try:
                    getattr(object, name)
                except AttributeError:
                    return self.__class__.prototype[name]
    
    class TestClass(Prototype):
        def __init__(self):
            pass
    
[/code]

コード例:  
[Pythonのメタプログラミング (メタクラス) を理解したい人のための短いコード片と禅問答 | TRIVIAL TECHNOLOGIES 4 @ats
のイクメン日記](http://coreblog.org/ats/lerning-metaclass-by-reading-javascript-like-
prototype-in-python/)


CC BY-SA 3.0
2017-12-27T12:34:36.673
40557
2017-12-28T01:41:24.183
2017-12-27T13:41:35.723
5246
5246
question
python
Pythonの__getattr__でreturnしていないのに値が戻ってくる?


元々のコードを書いた人は`__getattr__`が未定義の属性がアクセスされた時だけ呼び出されることを理解していないようです。これを理解していれば、次の２点がおかしなことに気づきます。

  1. 自分で.prototypeを定義済みなのに、Prototypeクラスの__getattr__メソッドでifで確認している。ここは必ずFalseになるから、到達不能コード。
  2. 同様に属性が未定義の時だけ通るのだから、getattr()で再チェックは不要。それもなぜ`object`クラスの属性？必ずAttributeErrorになるから、getattr()もtry-exceptionも意味がない。

従ってどちらの`getattr()`も、returnが不要なのではなく、そもそもこのコードパスは通らないのです。

ちなみに`.prototype`は`class
TestClass(Prototype):...`の定義が実行された時点でPrototypeStoreがインスタンス化されてセットされています。

ごく単純に「ある属性が見つからなかったら`.prototype`から探してくる」でいいのではないかと思います。書き直すとしたら次の通り。

[code]

    class Prototype(object):
        __metaclass__ = PrototypeMeta
    
        def __getattr__(self, name):
            return self.prototype[name]
    
[/code]

蛇足:
似たようなメソッドで`__getattribute__`があります。こちらは属性の定義済み・未定義に関わらず無条件に呼び出されます。オリジナルのコードを書いた人はこのメソッドと混同したのかもしれません。[みんな混乱している](https://stackoverflow.com/questions/3278077/difference-
between-getattr-vs-getattribute/38788326)のでほとんど同じ名前をつけたPythonの作者の責任も重いと思います。

JavaScriptのPrototypeを深く理解しているわけではないので、何か見落としているかもしれませんが、以上ご参考まで。


CC BY-SA 3.0
2017-12-28T01:21:55.257
40572
2017-12-28T01:41:24.183
2017-12-28T01:41:24.183
7837
7837
40557
answer