私は同様の混乱でこの質問に出くわしました.自分で答えた後、繁栄のためにここで私の発見を報告するのが賢明だと思われました.
ThiefMaster が既に指摘したように、「所有者」パラメーターは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')
既に確立したように、関数のowner
パラメーターは__get__
、クラス レベルで属性にアクセスするときに、__get__
関数が getattr 呼び出しをインターセプトすることを意味します。たまたま、 のコードは、クラス レベルでアクセスされたときにプロパティ自体を返すproperty
だけですが、何でもできます (静的な値を返すなど)。
とが同じように機能する__set__
とどうなるか想像してみてください。andメソッドは、インスタンス レベルに加えて、クラス レベルですべての and 呼び出しを__del__
インターセプトします。__set__
__del__
setattr
delattr
結果として、これは の "count" 属性MyClass
が事実上変更不可能であることを意味します。Java のような静的でコンパイルされた言語でのプログラミングに慣れている場合、アプリケーション コードでクラスを変更できないため、これはあまり興味深いものではないように思われます。しかし、Python ではそれが可能です。クラスはオブジェクトと見なされ、それらの属性を動的に割り当てることができます。たとえば、MyClass
がサードパーティ モジュールの一部であり、MyClass
アプリケーションにとってほぼ完全に完璧であるとしましょう ( のコード以外に他のコードがあると仮定しましょうcount
)。代わりに、インスタンスごとに常に 10 を返すようにします。次のことができます。
>>> MyClass.count = 10
>>> myinstance = MyClass()
>>> myinstance.count
10
__set__
への呼び出しが傍受された場合setattr(MyClass, 'count')
、実際に MyClass を変更する方法はありません。代わりに、コードはsetcount
それを傍受し、何もできませんでした。唯一の解決策は、MyClass のソース コードを編集することです。(サブクラスでそれを定義してもコードが呼び出されると思うので、サブクラスで上書きできるかどうかさえsetattr
わかりません。しかし、確信が持てません。ここではすでに反事実を扱っているので、私はしません。実際にテストする方法はありません。)
さて、あなたは「まさにそれが私の望みです! 私は自分のクラスの属性を意図的にユーザーに再割り当てさせたくなかったのです!」と言っているかもしれません。それに対して、私が言えることは、単純な記述子を使用してあなたが望んでいたことは不可能であるということだけです。私はあなたに上記の理由を説明します. クラス属性の再割り当てを許可することは、現在の Python のイディオムに沿ったものです。
あなたが本当に本当に読み取り専用のクラス属性を作りたいのなら、その方法を教えてくれるとは思いません。しかし、解決策がある場合、おそらくメタクラスを使用し、メタクラスを作成するかproperty
、setattrおよびdelattrのメタクラスのコードを変更する必要があります。しかし、これはディープ マジックであり、この回答 (および Python に関する私自身の能力) の範囲をはるかに超えています。