2012-05-12 19 views
0

記述子クラスがある:Pythonは `次のようにdescriptor`

class Des(object): 
    def __get__(self, instance, owner): ... 
    def __set__(self, instance, value): ... 
    def __delete__(self, instance): ... 

class Sub(object): 
    attr = Des() 

X = sub() 

質問

  1. 私はそれをどのように使用できるか、ownerの存在のポイントが表示されませんか?

  2. attrを読み取り専用にするには、__set__を省略することはできませんが、割り当てをキャッチして例外を発生させるように定義する必要があります。だからX.attr = 123は失敗しますが、__set__の引数にはownerが含まれていません。つまり、私はまだSub.attr = 123を実行できますか?

答えて

4

http://docs.python.org/reference/datamodel.html#implementing-descriptorsを参照してください:instanceは属性は所有者

経由でアクセスされたときに属性を介してアクセスしていない、またはなしたことをインスタンスである一方、

ownerは、常に所有者クラスです

ownerを使用する場合、クラスプロパティを作成する

class _ContentQueryProperty(object): 
    def __get__(self, inst, cls): 
     return Content.query.filter_by(type=cls.TYPE) 
+0

はい、私は「所有者」が何であるかを知っていますが、この「所有者」はどのように使用できますか?私は '所有者 'の存在の点を見ていない。そして私はあなたが私の2番目の質問に答えたとは思わない。 – Alcott

+0

それは一つです、upvoted。では、**読み取り専用**の問題はどうですか? – Alcott

+0

私の推測では、記述子はクラスレベルで記述されるのではなく、インスタンスレベルでのみ記述されることになります。 – ThiefMaster

1

私は同様の混乱で、この質問に出くわした、と私は自分自身のためにそれに答えた後、繁栄のためにここに私の調査結果を報告することが賢明と思われました。

ThiefMasterがすでに指摘しているように、「owner」パラメータはclasspropertyのような構成を可能にします。場合によっては、クラスにメソッド以外の属性としてマスクされたメソッドを持たせ、ownerパラメータを使用すると、通常の記述子でそれを行うことができます。

しかし、これは半分の質問です。 「読み取り専用」問題については、ここに私が見つけたものがあります:

私は最初に答えを見つけました:http://martyalchin.com/2007/nov/23/python-descriptors-part-1-of-2/。私は最初にそれを理解していなかったし、私の頭を包むのに5分ほどかかった。ついに私に納得させたのは、例を思い付くことでした。

最も一般的な記述子を考えてみましょう:property。変数countにアクセスした回数であるプロパティcountを使って、簡単なサンプルクラスを使いましょう。

class MyClass(object): 
    def __init__(self): 
     self._count = 0 
    @property 
    def count(self): 
     tmp = self._count 
     self._count += 1 
     return tmp 
    @count.setter 
    def setcount(self): 
     raise AttributeError('read-only attribute') 
    @count.deleter 
    def delcount(self): 
     raise AttributeError('read-only attribute') 

我々はすでに確立されてきたように、__get__機能のownerパラメータを使用すると、クラスレベルで属性にアクセスすると、__get__機能はGETATTR呼び出しをインターセプトすることを意味します。そのような場合、propertyのコードはクラスレベルでアクセスされると単純にreturns the property itselfになりますが、何らかの静的な値を返すような何かを行うことができます。

ここで、__set____del__が同じように動作した場合を考えてみましょう。 __set____del__メソッドは、インスタンスレベルに加えて、クラスレベルですべてsetattrdelattrの呼び出しをインターセプトします。

これは、MyClassの「カウント」属性が実質的にの変更不可能なであることを意味します。 Javaのようなコンパイルされた静的な言語でプログラミングすることに慣れていた場合、アプリケーションコード内でクラスを変更することはできないので、これはあまり面白く見えません。しかしPythonでは、そうすることができます。クラスはオブジェクトとみなされ、それらの属性のいずれかを動的に割り当てることができます。たとえば、MyClassがサードパーティのモジュールの一部であり、MyClassがアプリケーションにほぼ完全に対応しているとします(ただし、countのコード以外に他のコードがあるとします)。代わりに、すべての単一のインスタンスに対して常に10を返すようにします。私たちは、次の操作を行うことができます:setattr(MyClass, 'count')への呼び出しをインターセプト

>>> MyClass.count = 10 
>>> myinstance = MyClass() 
>>> myinstance.count 
10 

__set__場合は、実際にMyClassのを変更する方法はありません。代わりに、setcountのコードはそれを傍受し、何もできませんでした。唯一の解決策は、MyClassのソースコードを編集することです。 (サブクラスでそれを定義すると、setattrコードがまだ呼び出されると思うので、サブクラスで上書きできるかどうかは確かではありませんが、私はまだ分かりません。 )

「これはまさに私が欲しいものです!私はを意図的には私のクラスの属性を再割り当てしたくありませんでした!そのために、私が言うことができるのは、素朴な記述子を使ってあなたが望むものは不可能であるということです。私は上記の推論にあなたを導きます。クラス属性を再割り当てできるようにすることは、現在のPythonのやり方に沿ったものです。あなたが本当に、が本当には読み取り専用のクラス属性を作成したい場合は

、私はどのようにあなたを伝えることができるとは思いません。解決策がある場合でも、それはおそらく、メタクラスを使っ伴うだろうとメタクラスpropertyを作成したりSETATTRと delattr のメタクラスのコードを変更するのいずれか。しかし、これはディープマジックであり、この答えの範囲をはるかに超えています(そして自分自身のPythonの能力)。

0

所有者はインスタンスのクラスであり、便宜上提供されています。あなたはいつものインスタンスからそれを計算することができます。

owner = instance.__class__ 
+0

これは、インスタンス上の記述子にアクセスする場合にのみ当てはまります。クラスにアクセスすると、 'instance'は' None'になり、 'owner'はあなたがアクセスしたクラスになります(あなたのコードが意味する' NoneType'ではなく)。さて、ほとんどの種類の記述子では、クラスアクセスを気にしませんが、それはおそらく重要ではありませんが、気にしていれば重要です。 – Blckknght

+0

私は同意します - あなたはr値を介して記述子にアクセスすると仮定します。しかし、l値アクセスを試みると直接辞書アクセスが試みられ、これが記述子を上書きしてしまうようです。 r値アクセスであっても、クラス属性を別の名前で保存しない限り、返すものについては例外が発生する可能性があります。 – resigned

+0

ドキュメント内に記載されているように: – resigned

1

あなたはこの例を試すことができます。

# the descriptor protocol defines 3 methods: 
# __get__() 
# __set__() 
# __delete__() 

# any class implementing any of the above methods is a descriptor 
# as in this class 
class Trace(object): 
    def __init__(self, name): 
     self.name = name 

    def __get__(self, obj, objtype): 
     print "GET:" + self.name + " = " + str(obj.__dict__[self.name]) 
     return obj.__dict__[self.name] 

    def __set__(self, obj, value): 
     obj.__dict__[self.name] = value 
     print "SET:" + self.name + " = " + str(obj.__dict__[self.name]) 

# define the attributes of your class (must derive from object) 
# to be references to instances of a descriptor 

class Point(object): 
# NOTES: 
# 1. descriptor invoked by dotted attribute access: A.x or a.x 
# 2. descripor reference must be stored in the class dict, not the instance dict 
# 3. descriptor not invoked by dictionary access: Point.__dict__['x'] 

    x = Trace("x") 
    y = Trace("y") 

    def __init__(self, x0, y0): 
     self.x = x0 
     self.y = y0 

    def moveBy(self, dx, dy): 
     self.x = self.x + dx  # attribute access does trigger descriptor 
     self.y = self.y + dy 


# trace all getters and setters  
p1 = Point(15, 25) 
p1.x = 20 
p1.y = 35 
result = p1.x 
p2 = Point(16, 26) 
p2.x = 30 
p2.moveBy(1, 1) 
1

限り、読み取り専用のプロパティが懸念しているとして(上記の議論を参照)、以下の例では、どのようにそのが完了を示します:

############################################################ 
# 
# descriptors 
# 
############################################################ 

# define a class where methods are invoked through properties 
class Point(object): 
    def getX(self): 
    print "getting x" 
    return self._x 

    def setX(self, value): 
    print "setting x" 
    self._x = value 

    def delX(self): 
    print "deleting x" 
    del self._x 

    x = property(getX, setX, delX) 

p = Point() 
p.x = 55 # calls setX 
a = p.x  # calls getX 
del p.x  # calls delX 

# using property decorator (read only attributes) 
class Foo(object): 
    def __init__(self, x0, y0): 
    self.__dict__["myX"] = x0 
    self.__dict__["myY"] = y0 

    @property 
    def x(self): 
    return self.myX 

f = Foo(4,6) 
print f.x 
try: 
    f.x = 77  # fails: f.x is read-only 
except Exception,e: 
    print e 
0
  1. __set__方法は、インスタンスの属性を変更することになっています。しかし、すべてのインスタンスによって共有され、したがってそのクラスに存在する属性、たとえばクラス属性を変更する場合はどうすればよいでしょうか?これは、クラスへのアクセス権がある場合にのみ実行できます。したがって、owner引数です。

  2. はい、クラスを介して属性に割り当てると、プロパティ/記述子を上書きできます。 Pythonは動的言語なので、これは設計によるものです。

長いこと前に尋ねられましたが、質問に答える希望。