22

Python クラスでは、使用する__setattr__と、このクラス (または基本クラス) で定義されたプロパティよりも優先されます。次のコードを検討してください。

class Test(object):
    def get_x(self):
        x = self._x
        print "getting x: %s" % x
        return x
    def set_x(self, val):
        print "setting x: %s" % val
        self._x = val
    x = property(get_x, set_x)
    def __getattr__(self, a):
        print "getting attr %s" % a
        return -1
    def __setattr__(self, a, v):
        print "setting attr %s" % a

クラスを作成して設定しようとすると、代わりx__setattr__が呼び出されますset_x

>>> test = Test()
>>> test.x = 2
setting attr x
>>> print test.x
getting attr x_
getting x: -1
-1

私が達成したいの__setattr__は、関連するプロパティがない場合にのみ実際のコードが呼び出されることtest.x = 2ですset_xax」がであるかどうかを手動で確認することでこれを簡単に達成できることはわかっていますが__setattr__、これでは設計が不十分になります。__setattr__クラスとすべての基本クラスで定義されたすべてのプロパティに対して適切な動作を保証するより賢い方法はありますか?

4

4 に答える 4

35

Python が属性に使用する検索順序は次のようになります。

  1. __getattribute____setattr__
  2. 次のようなデータ記述子property
  3. オブジェクトのインスタンス変数__dict__(属性を設定する場合、ここで検索が終了します)
  4. 非データ記述子 (メソッドなど) およびその他のクラス変数
  5. __getattr__

が最初に並んでいるので__setattr__、クラスのすべての属性設定を処理したくない場合を除き、スマートにする必要があります。特定のセット属性のみを処理するようにするか、一部の属性セットを除くすべてを処理するようにするかの 2 つの方法のいずれかでスマートにすることができます。処理したくないものについては、 を呼び出しますsuper().__setattr__

サンプルクラスでは、「「x」を除くすべての属性」を処理するのがおそらく最も簡単です。

def __setattr__(self, name, value):
    if name == "x":
        super(Test, self).__setattr__(name, value)
    else:
        print "setting attr %s" % name
于 2013-04-01T20:21:46.947 に答える
9

これは防弾ソリューションではありませんが、あなたが示唆したように、クラスの属性から(クラスオブジェクトを使用して)オブジェクトsetattrにアクセスしようとすることで、プロパティが編集されているかどうかを確認できます。propertygetattr

class Test(object):

    def get_x(self):
        x = self._x
        print "getting x: %s" % x
        return x
    def set_x(self, val):
        print "setting x: %s" % val
        self._x = val
    x = property(get_x, set_x)

    @property  # no fset
    def y(self):
        print "getting y: 99"
        return 99

    def __getattr__(self, a):
        print "getting attr %s" % a
        return -1
    def __setattr__(self, a, v):
        propobj = getattr(self.__class__, a, None)
        if isinstance(propobj, property):
            print "setting attr %s using property's fset" % a
            if propobj.fset is None:
                raise AttributeError("can't set attribute")
            propobj.fset(self, v)
        else:
            print "setting attr %s" % a
            super(Test, self).__setattr__(a, v)


test = Test()
test.x = 2
print test.x
#test.y = 88  # raises AttributeError: can't set attribute
print test.y
test.z = 3
print test.z

編集:@Blckknghtの回答に見られるように、に置き換えself.__dict__[a] = vられましたsuper(Test, self).__setattr__(a, v)

于 2013-04-01T20:20:26.837 に答える
0

この問題に何度か遭遇しましたが、私の推奨する解決策は次のとおりです。

  • プロパティごとに、デコレータを使用せずに、同じよう[property]に定義get_[property]して機能しますset_[property]
  • __getattr__および を変更し__setattr__て、これらの関数の存在を確認し、使用可能な場合は使用するようにします。

最小限の例を次に示します。

class SmartSetter(object):

  def __init__(self,name):
    self.set_name(name)

  def get_name(self):
    #return the name property
    return self._name

  def set_name(self,value):
    #set the name property
    self._name = value

  def __getattr__(self,key):
    #first condition is to avoid recursive calling of this function by
    #__setattr__ when setting a previously undefined class attribute.
    if not key.startswith('get_') and hasattr(self,'get_'+key):
      return getattr(self,'get_'+key)()
    #implement your __getattr__ magic here...
    raise AttributeError("no such attribute: %s" % key)

  def __setattr__(self,key,value):
    try:
      return getattr(self,'set_'+key)(value)
    except AttributeError:
      #implement your __setattr__ magic here...
      return super(SmartSetter,self).__setattr__(key,value)

if __name__ == '__main__':
  smart_setter = SmartSetter("Bob the Builder")

  print smart_setter.name
  #Will print "Bob the Builder"

  smart_setter.name = "Spongebob Squarepants"

  print smart_setter.name
  #Will print "Spongebob Squarepants"

このメソッドの利点は、「getter」メソッドと「setter」メソッドを持つ属性を除くすべての属性の通常の動作が維持されることと、プロパティを追加または削除するときに関数__getattr__と関数を変更する必要がないことです。__setattr__

于 2014-03-22T22:56:21.470 に答える