2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Pythonの@propertyの仕組みについて調べて考えてみた(getterのみ)

Posted at

概要

Pythonのクラスでpropertyデコレータを使うとどうしてメソッド呼び出しをせずに値が取れるのか調べた。

実験

環境

Pythonのバージョン

Python 3.5.2

使ったコード

class Property(object):
    "Emulate PyProperty_Type() in Objects/descrobject.c"

    def __init__(self, fget=None, fset=None, fdel=None, doc=None):
        self.fget = fget
        self.fset = fset
        self.fdel = fdel
        if doc is None and fget is not None:
            doc = fget.__doc__
        self.__doc__ = doc

    def __get__(self, obj, objtype=None):
        #print(obj)
        #print(objtype)
        if obj is None:
            return self
        if self.fget is None:
            raise AttributeError("unreadable attribute")
        return self.fget(obj)

class Hoge:
    def __init__(self):
        self._foo = 'foo!'
    @Property
    def foo(self):
        return self._foo
    

if __name__ == '__main__':
    h = Hoge()
    print(h.foo)

Propertyクラスの実装はhttps://docs.python.jp/3/howto/descriptor.html#properties のもので、デバッグ用にprintを追加し、__init____get__以外のメソッドを削った。
実行すると以下のようになる。

$ python property.py
foo!

printのコメントアウトを外してみる

コメントアウトを外して実行すると下のような結果になり、どうにかこうにかHogeが渡されてきているのが分かる。

$ python property.py
<__main__.Hoge object at 0x00000280356C87F0>
<class '__main__.Hoge'>
foo!

@Propertyを消してみる

$ python property.py
<bound method Hoge.foo of <__main__.Hoge object at 0x000002209BB687B8>>

メソッドは当然実行されていない。
print(h.foo) => print(h.foo()) に変更すると、

$ python property.py
foo!

元と同じ出力になる。

考察

https://docs.python.jp/3/reference/compound_stmts.html#function によると、

    @Property
    def foo(self):
        return self._foo

は、

    def foo(self):
        return self._foo
    foo = Property(foo)

とほぼ等価らしい。
これでPropertyクラスのfgetにfooが入る。

https://docs.python.jp/3/howto/descriptor.html#invoking-descriptors によると、呼び出し側の h.footype(h).__dict__['foo'].__get__(h, type(h))object.__getattribute__によって変換されるらしい。
type(h).__dict__['foo']は上のデコレータでPropertyのインスタンスを代入したのと同じなので、__get__()が、obj=h, objtype=type(h)を引数として呼び出される。
こうしてfooメソッドにselfを渡して実行することができ、その結果が値として返ってくるのでprint(h.foo)で値を表示できた。

2
1
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
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?