3

これの正しいイディオムは何ですか?

(オプションで) dict から初期化できるプロパティを含むオブジェクトを定義したい (dict は JSON に由来する; 不完全かもしれない)。後で、セッターを介してプロパティを変更することがあります。実際には13以上のプロパティがあり、デフォルトのゲッターとセッターを使用できるようにしたいのですが、この場合はうまくいかないようです:

しかし、すべてに対して明示的な記述子を記述する必要はありません。prop1... propn また、デフォルトの割り当てを__init__()アクセサーから出し入れしたいのですが、明示的な記述子が必要になります。

最もエレガントなソリューションは何ですか? __init__()(すべてのセッター呼び出しをメソッド/クラスメソッドから出し入れする以外に_make()?)

[削除されたコメント デフォルト記述子を使用する badprop のコードは、以前の SO ユーザーによるコメントによるもので、デフォルト セッターを提供するという印象を与えました。しかし、そうではありません-セッターは未定義であり、必然的にスローされAttributeErrorます。]

class DubiousPropertyExample(object):
    def __init__(self,dct=None):
        self.prop1 = 'some default'
        self.prop2 = 'other default'
        #self.badprop = 'This throws AttributeError: can\'t set attribute'
        if dct is None: dct = dict() # or use defaultdict 
        for prop,val in dct.items():
            self.__setattr__(prop,val)

    # How do I do default property descriptors? this is wrong
    #@property
    #def badprop(self): pass

    # Explicit descriptors for all properties - yukk
    @property
    def prop1(self): return self._prop1
    @prop1.setter
    def prop1(self,value): self._prop1 = value

    @property
    def prop2(self): return self._prop2
    @prop2.setter
    def prop2(self,value): self._prop2 = value

dub = DubiousPropertyExample({'prop2':'crashandburn'})

print dub.__dict__
# {'_prop2': 'crashandburn', '_prop1': 'some default'}

self.badprop = ...5 行目のコメントを外してこれを実行すると、失敗します。

self.badprop = 'This throws AttributeError: can\'t set attribute'

AttributeError: 属性を設定できません

[いつものように、ディスクリプタ、暗黙のディスクリプタに関する SO の投稿を読み、initから呼び出します]

4

3 に答える 3

7

プロパティの仕組みを少し誤解していると思います。「デフォルトセッター」はありません。AttributeErroron 設定をスローするのは、それが通常の属性ではなくプロパティでbadpropあることをまだ認識していないからではありませんbadprop(その場合、エラーなしで属性を設定するだけです。これは、通常の属性の動作であるためです)。のセッターは提供されbadpropず、ゲッターのみが提供されました。

これを見てください:

>>> class Foo(object):
    @property
    def foo(self):
        return self._foo
    def __init__(self):
        self._foo = 1


>>> f = Foo()
>>> f.foo = 2

Traceback (most recent call last):
  File "<pyshell#12>", line 1, in <module>
    f.foo = 2
AttributeError: can't set attribute

__init__インスタンスが構築された後は、外部からでもそのような属性を設定することはできません。を使用するだけの場合@property、取得できるのは読み取り専用プロパティです (事実上、属性 read のように見えるメソッド呼び出し)。

ゲッターとセッターで行っているのが、読み取り/書き込みアクセスを同じ名前の属性にリダイレクトすることだけであるが、アンダースコアが前に付いている場合、はるかに簡単なことは、プロパティを完全に削除して、通常の属性を使用することです. Python は Java ではありません (そして、Java であっても、明らかに public の getter/setter を使用したプライベート フィールドの利点については確信が持てません)。外部から直接アクセスできる属性は、「パブリック」インターフェースの完全に合理的な部分です。属性が読み書きされるたびに何らかのコードを実行する必要があることが後でわかった場合は、インターフェースを変更せずにそれをプロパティにすることができます(これは実際には記述子が本来意図していたものでありそのため、すべての属性に対して Java スタイルのゲッター/セッターを書き始めることができます)。

属性の名前を変更する以外のプロパティで実際に何かを行っていて、属性を読み取り専用にたい場合、おそらく最善の策は__init__、基になるデータ属性をアンダースコアで直接設定するものとして初期化を扱うことです先頭に追加。次に、クラスを なしで簡単に初期化できます。AttributeErrorsその後、属性が読み取られると、プロパティが機能します。

属性の名前を変更する以外に実際にプロパティで何かを行っていて、属性を読み取りおよび書き込み可能にしたい場合は、それらを取得/設定したときに何が起こるかを実際に指定する必要があります。各属性に独立したカスタム動作がある場合、各属性にゲッターとセッターを明示的に提供するよりも効率的な方法はありません。

すべての getter/setter でまったく同じ (または非常に類似した) コードを実行している場合 (実際の属性名にアンダースコアを追加するだけではない場合)、それがすべてを書き出すことに反対する理由です (当然のことです!)。 、、、およびのいくつかを実装することで__getattr__、より良いサービスが提供される場合があります。これらを使用すると、属性ごとに 2 つの関数 (取得/設定) を使用するのではなく、属性の読み取り/書き込みを(属性の名前をパラメーターとして) 毎回同じコードにリダイレクトできます。__getattribute____setattr__

于 2012-08-22T02:44:29.420 に答える
4

これを実行する最も簡単な方法は、インスタンス メンバーとして設定する必要がある、解析された JSON dict 内の任意のキーにアクセスできるように__getattr__実装することです。別の方法として、解析された JSON を__setattr__呼び出すこともできますが、これは、入力 dict がインスタンスのメンバーを踏みにじる可能性があることを意味するため、実際には最善の方法ではありません。update()self.__dict__

セッターとゲッターに関しては、問題の値を直接設定または取得する以外に実際に何か特別なことを行う場合にのみ作成する必要があります。Python は Java (または C++ など) ではありません。これらの言語で一般的なプライベート/セット/取得パラダイムを模倣しようとするべきではありません。

于 2012-08-22T02:36:38.653 に答える