2016-10-09 10 views
2

Python descriptor How-Toは、記述子に関してproperty()を実装する方法を記述しています。私は__get__メソッドの最初のifブロックの理由を理解していません。どんな状況下でobjNoneになりますか?それから何が起こるはずですか? __get____del__の方法でそれをチェックしないのはなぜですか?プロパティ()の記述子プロトコルの実装

コードは少し長めですが、単なるスニペットではなく完全なコードを与える方がよいでしょう。疑わしい行がマークされています。 descriptor documentationから

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): 
     # =====>>> What's the reason of this if block? <<<===== 
     if obj is None: 
      return self 
     if self.fget is None: 
      raise AttributeError("unreadable attribute") 
     return self.fget(obj) 

    def __set__(self, obj, value): 
     if self.fset is None: 
      raise AttributeError("can't set attribute") 
     self.fset(obj, value) 

    def __delete__(self, obj): 
     if self.fdel is None: 
      raise AttributeError("can't delete attribute") 
     self.fdel(obj) 

    def getter(self, fget): 
     return type(self)(fget, self.fset, self.fdel, self.__doc__) 

    def setter(self, fset): 
     return type(self)(self.fget, fset, self.fdel, self.__doc__) 

    def deleter(self, fdel): 
     return type(self)(self.fget, self.fset, fdel, self.__doc__) 

答えて

2

このテストを終了する別のバージョンを作成すると、その効果を確認できます。あなたが投稿したコードを使用するクラスPropertyを作成し、ブロックを外す別のBadPropertyをifブロックにしました。

class Foo(object): 
    @Property 
    def good(self): 
     print("In good getter") 
     return "good" 

    @good.setter 
    def good(self, val): 
     print("In good setter") 

    @BadProperty 
    def bad(self): 
     print("In bad getter") 
     return "bad" 

    @bad.setter 
    def bad(self, val): 
     print("In bad setter") 

類似点と相違点は、この例で見ることができる:私はこのクラスを作っ

>>> x = Foo() 

# same 
>>> x.good 
In good getter 
'good' 
>>> x.bad 
In bad getter 
'bad' 
>>> x.good = 2 
In good setter 
>>> x.bad = 2 
In bad setter 

# different! 
>>> Foo.good 
<__main__.Property object at 0x0000000002B71470> 
>>> Foo.bad 
In bad getter 
'bad' 
ifブロックの効果は、それを介してアクセスされた場合に生プロパティオブジェクト自体を返すことである

クラス。このチェックを行わないと、クラスを介してディスクリプタにアクセスしてもgetterが呼び出されます。

__set____del__のメソッドでは、クラスの属性の設定/削除(インスタンスのみ)で記述子プロトコルがまったく呼び出されないため、このようなチェックは必要ありません。これはドキュメントから完全にはっきりとは分かりませんが、__get__の記述と__set__/__del__の記述との違いは、__get__の属性が「__set__/__del__」の場合はthe docsになりますインスタンスの属性のみを設定/削除します。

+0

したがって、基本的にクラスの(静的な)定義からの記述子を実際のインスタンスにコピー/挿入するのは...? – Michael

+1

@Michael:いいえ、記述子のメカニズムがクラスでアクティブにならないようにして、インスタンス上でのみ*アクティブにすることです。どちらの場合でも '__get__'が呼び出されるのがデフォルトの動作です。 – BrenBarn

1

:呼び出しの

詳細objオブジェクトまたはクラスであるかどうかに依存します。

基本的に、インスタンスではtype(b).__dict__['x'].__get__(b, type(b))と記述され、クラスではB.__dict__['x'].__get__(None, B)と呼ばれます。 obj is Noneは、ゲッターがクラスから呼び出されたことを意味し、インスタンスではありません。

この機械は、たとえばclassmethodsを実装するために使用されます。

__set____delete__は、obj is Noneをこのように呼び出すことはできないため、チェックしません。クラスから呼び出されたときにのみ__get__が呼び出されます。 cls.prop = 2またはdel cls.propを実行すると、__set__または__delete__を呼び出さずにプロパティオブジェクトを直接上書きまたは削除します。

関連する問題