[Python3.4に基づいて書かれた回答; メタクラスの構文は2で異なりますが、この手法は引き続き機能すると思います]
これはメタクラスで行うことができます...ほとんどの場合。Dappawitはほとんど機能しますが、欠陥があると思います。
class MetaFoo(type):
@property
def thingy(cls):
return cls._thingy
class Foo(object, metaclass=MetaFoo):
_thingy = 23
これにより、Fooのクラスプロパティが取得されますが、問題があります...
print("Foo.thingy is {}".format(Foo.thingy))
# Foo.thingy is 23
# Yay, the classmethod-property is working as intended!
foo = Foo()
if hasattr(foo, "thingy"):
print("Foo().thingy is {}".format(foo.thingy))
else:
print("Foo instance has no attribute 'thingy'")
# Foo instance has no attribute 'thingy'
# Wha....?
ここで何が起こっているのですか?インスタンスからクラスプロパティにアクセスできないのはなぜですか?
答えが何であるかを見つける前に、私はかなり長い間これに頭を悩ませていました。Python @propertiesは記述子のサブセットであり、記述子のドキュメント(強調私のもの)から:
属性アクセスのデフォルトの動作は、オブジェクトのディクショナリから属性を取得、設定、または削除することです。たとえば、、、、でa.x始まり、メタクラスを除外する基本クラスまで続くルックアップチェーンa.__dict__['x']がありtype(a).__dict__['x']ます。type(a)
したがって、メソッドの解決順序には、クラスプロパティ(またはメタクラスで定義されているその他のもの)は含まれません。動作が異なる組み込みのプロパティデコレータのサブクラスを作成することは可能ですが、(引用が必要です)開発者がそのようにする正当な理由(私にはわかりません)があるという印象を受けました。
それは私たちが運が悪いという意味ではありません。クラス自体のプロパティに問題なくアクセスできます...そしてtype(self)、インスタンス内からクラスを取得できます。これを使用して、@propertyディスパッチャーを作成できます。
class Foo(object, metaclass=MetaFoo):
_thingy = 23
@property
def thingy(self):
return type(self).thingy
Foo().thingyこれで、クラスとインスタンスの両方で意図したとおりに機能します。また、派生クラスがその基礎となるものを置き換えた場合も、正しいことを継続します_thingy(これは、私が最初にこのハントに参加したユースケースです)。
これは私にとって100%満足できるものではありません。メタクラスとオブジェクトクラスの両方でセットアップを行う必要があると、DRYの原則に違反しているように感じます。ただし、後者は1行のディスパッチャにすぎません。私はそれが存在することで大体大丈夫です、そしてあなたが本当に望むならおそらくあなたはそれをラムダか何かに圧縮することができます。