2016-03-16 51 views
20

Python 3.4以降、DynamicClassAttributeという記述子があります。ドキュメントの状態:DynamicClassAttributeとは何ですか?どうすれば使用できますか?

types.DynamicClassAttribute(fget=None, fset=None, fdel=None, doc=None)

ルート__getattr__にクラスのアクセス属性。

これは、インスタンスおよびクラスを通してアクセスするときに異なる動作をする属性を定義するために使用される記述子です。インスタンスのアクセスは通常のままですが、クラスを介した属性へのアクセスはクラスの__getattr__メソッドにルーティングされます。これは、AttributeErrorを上げることによって行われます。

これは、インスタンス上でプロパティをアクティブにでき、同じ名前のクラスに仮想属性を持つことができます(例として列挙を参照)。

バージョン3.4の新機能

それは明らかにenum moduleで使用されます。

# DynamicClassAttribute is used to provide access to the `name` and 
# `value` properties of enum members while keeping some measure of 
# protection from modification, while still allowing for an enumeration 
# to have members named `name` and `value`. This works because enumeration 
# members are not set directly on the enum class -- __getattr__ is 
# used to look them up. 

@DynamicClassAttribute 
def name(self): 
    """The name of the Enum member.""" 
    return self._name_ 

@DynamicClassAttribute 
def value(self): 
    """The value of the Enum member.""" 
    return self._value_ 

私はそのenums are a little specialを実現するが、私は、これはDynamicClassAttributeとどのように関係するかを理解していません。これらの属性がの動的なであることは、通常のプロパティとどのように違うのですか。DynamicClassAttributeを私の利点に使用するにはどうすればよいですか?

答えて

12

新バージョン:

私はそれを少し書き換えることにしましたので、私は前の回答と少しがっかりしました:

まず、それは非常に見えること、the source code of DynamicClassAttributeを見て、あなたはおそらくわかります通常のpropertyとよく似ています。 __get__ -methodを除い:

def __get__(self, instance, ownerclass=None): 
    if instance is None: 
     # Here is the difference, the normal property just does: return self 
     if self.__isabstractmethod__: 
      return self 
     raise AttributeError() 
    elif self.fget is None: 
     raise AttributeError("unreadable attribute") 
    return self.fget(instance) 

それでは、これが意味するクラスにではなくselfを返すAttributeErrorを上げるあなたはDynamicClassAttributeにアクセスする場合(つまり、抽象的ではない)ということです。例の場合if instance:Trueとなり、__get__property.__get__と同じです。トレースバック(最新の呼び出しの最後)

自身のためではないこと -

from types import DynamicClassAttribute 
class Fun(): 
    @DynamicClassAttribute 
    def has_fun(self): 
     return False 
Fun.has_fun 

はAttributeError:属性を呼び出すときだけ見えるAttributeErrorに解決し、通常のクラスの場合

「クラス属性の参照」プロシージャを参照するまでは非常に役に立ちます。metaclassを使用すると、this blogにこの素晴らしい画像が見つかりました)。 属性がAttributeErrorを生成し、そのクラスがメタクラスを持つ場合、Pythonはmetaclass.__getattr__メソッドを見て、それが属性を解決できるかどうかを調べます。これを最小限の例で説明すると、次のようになります。

from types import DynamicClassAttribute 

# Metaclass 
class Funny(type): 

    def __getattr__(self, value): 
     print('search in meta') 
     # Normally you would implement here some ifs/elifs or a lookup in a dictionary 
     # but I'll just return the attribute 
     return Funny.dynprop 

    # Metaclasses dynprop: 
    dynprop = 'Meta' 

class Fun(metaclass=Funny): 
    def __init__(self, value): 
     self._dynprop = value 

    @DynamicClassAttribute 
    def dynprop(self): 
     return self._dynprop 

ここに「動的」部分があります。あなたがクラスにdynpropを呼び出した場合には、メタで検索して返しますメタのdynprop

Fun.dynprop 

出力します

search in meta 
'Meta' 

だから我々はmetaclass.__getattr__を呼び出し、元の属性を返された(でした新しいプロパティと同じ名前で定義されています)。

Fun -instanceのdynpropが返されるインスタンスにいる間:

Fun('Not-Meta').dynprop 

我々はオーバーライドされた属性を取得:このから

'Not-Meta' 

私の結論は、あなたがしたい場合DynamicClassAttributeが重要であること、ですサブクラスがメタクラスで使用されているのと同じ名前の属性を持つことを許可します。あなたはインスタンス上でそれをシャドウしますが、あなたがクラスでそれを呼び出すと、それはまだアクセス可能です。私は古いバージョンでEnumの行動に行きました

ので、私はここでそれを左:

旧バージョン

DynamicClassAttributeがあれば(私はその点には本当にわからないんだけど)だけで便利ですサブクラスに設定されている属性とベースクラスのプロパティの間に名前の競合がある可能性があります。

属性検索が若干異なるあるので、これはメタクラス(this blog postで見つけることができますクラス属性が呼び出される方法についての素敵な説明)を使用せずに動作しませんので、あなたは、メタクラスに関する少なくともいくつかの基本を知っておく必要がありますメタクラスで

は、あなたが持っていると仮定します。その後、

class Funny(type): 
    dynprop = 'Very important meta attribute, do not override' 

class Fun(metaclass=Funny): 
    def __init__(self, value): 
     self._stub = value 

    @property 
    def dynprop(self): 
     return 'Haha, overridden it with {}'.format(self._stub) 

およびコール:

Fun.dynprop 

プロパティ0x1b3d9fd19a8

と我々が得るインスタンス上で:

Fun(2).dynprop 

は、それが失われています...

悪い

'ハハ、2でそれを上書き'。さんは__getattr__(フォールバック)を実装し、dynpropDynamicClassAttributeように実装してみましょう:しかし、私たちはmetaclass特別なルックアップを使用することができます待ちます。それによると、その目的だというdocumentationだから - それはクラスで呼ばれていた場合__getattr__にフォールバックする:今、私たちは、クラス属性にアクセス

from types import DynamicClassAttribute 

class Funny(type): 
    def __getattr__(self, value): 
     print('search in meta') 
     return Funny.dynprop 

    dynprop = 'Meta' 

class Fun(metaclass=Funny): 
    def __init__(self, value): 
     self._dynprop = value 

    @DynamicClassAttribute 
    def dynprop(self): 
     return self._dynprop 

Fun.dynprop 

印刷した:

search in meta 
'Meta' 

metaclass.__getattr__が呼び出され、元の属性(新しいプロパティと同じ名前で定義されています)が返されました。

とインスタンスのために:

'Not-Meta' 

まあ、我々は、インスタンスを作成せずに、以前に定義されているがオーバーライドされた属性にメタクラスを使用して再ルーティングすることができます考慮あまりにも悪くはない。

Fun('Not-Meta').dynprop 

我々はオーバーライドされた属性を取得します。この例では、サブクラスの属性を定義Enumで行われ、反対、次のとおりです。

from enum import Enum 

class Fun(Enum): 
    name = 'me' 
    age = 28 
    hair = 'brown' 

と、デフォルトではこれら後で定義された属性にアクセスしたいです。

Fun.name 
# <Fun.name: 'me'> 

いますが、(変数が実際に持っている名前を返します)DynamicClassAttributeと定義したname属性にアクセスできるようにしたい:それ以外の場合はどのようにあなたが28の名前にアクセスする可能性があるため

Fun('me').name 
# 'name' 

を?

Fun.hair.age 
# <Fun.age: 28> 
# BUT: 
Fun.hair.name 
# returns 'hair' 

違いをご確認ください。なぜ2番目のものは<Fun.name: 'me'>を返さないのですか?これはDynamicClassAttributeの使用によるものです。元のプロパティをシャドーできますが、後で再び解放します。この動作は、私の例で示した動作とは逆の動作で、少なくとも__new____prepare__の使用が必要です。しかし、あなたはそれが正確にどのように動作しているかを知る必要があります。多くのブログやスタックオーバーフローで説明されていますので、私ができるよりもはるかに説明できます。私は短い順序でそれを解決することができます)。

実際のユースケースは1つがpropablyいくつかを考えることができスパースが、与えられた時間であるかもしれない...

非常に素晴らしい議論DynamicClassAttributeの文書に:"we added it because we needed it"

関連する問題