Pythonクラスに__setattr__とディスクリプタの両方を使用する

私はカスタム属性へのアクセスを提供するために __ setattr __ __ getattr __ を使用するPythonクラスを作成しています。

しかし、いくつかの属性は汎用的に扱うことができないので、それらの記述子を使用したいと考えていました。

ディスクリプタの場合、ディスクリプタの __ get __ はインスタンス __ getattr __ のために呼び出されますが、属性に割り当てるときは __ setattr __ >は、記述子 __ set __ を使用して呼び出されます。

例:

class MyDesc(object):

    def __init__(self):
        self.val = None

    def __get__(self, instance, owner):
        print "MyDesc.__get__"
        return self.val

    def __set__(self, instance, value):
        print "MyDesc.__set__"
        self.val = value

class MyObj(object):

    foo = MyDesc()

    def __init__(self, bar):
        object.__setattr__(self, 'names', dict(
            bar=bar,
        ))
        object.__setattr__(self, 'new_names', dict())

    def __setattr__(self, name, value):
        print "MyObj.__setattr__ for %s" % name
        self.new_names[name] = value

    def __getattr__(self, name):
        print "MyObj.__getattr__ for %s" % name

        if name in self.new_names:
            return self.new_names[name]

        if name in self.names:
            return self.names[name]

        raise AttributeError(name)

if __name__ == "__main__":
    o = MyObj('bar-init')

    o.bar = 'baz'
    print o.bar

    o.foo = 'quux'
    print o.foo

プリントする:

MyObj.__setattr__ for bar
MyObj.__getattr__ for bar
baz
MyObj.__setattr__ for foo
MyDesc.__get__
None

記述子の __ set __ は呼び出されません。

Since the __setattr__ definition isn't just overriding behaviour for a limited set of names, there's no clear place that it can defer to object.__setattr__

可能であれば、記述子を使用する属性に割り当てることを推奨する方法はありますか?それ以外の場合は __ setattr __

5

2 答え

私は自動的にこれをマークする仕組みを持つことでこれにアプローチすると思います 記述子を各クラスに追加し、呼び出すように __ setattr __ をラップします これらの名前のオブジェクトの通常の動作。

This can be easily achieved with a metaclass (and a decorator for __setattr__

def setattr_deco(setattr_func):
    def setattr_wrapper(self, attr, value):
        if attr in self._descriptors:
            return object.__setattr__(self, attr, value)
        return setattr_func(self, attr, value)
    return setattr_wrapper

class MiscSetattr(type):
    def __new__(metacls, name, bases, dct):
        descriptors = set()
        for key, obj in dct.items():
            if key == "__setattr__":
                dct[key] = setattr_deco(obj)
            elif hasattr(obj, "__get__"):
                descriptors.add(key)
        dct["_descriptors"] = descriptors
        return type.__new__(metacls, name, bases, dct)

# and use MiscSetattr as metaclass for your classes
4
追加された

可能な方法の1つ:

def __setattr__(self, name, value):
    print "MyObj.__setattr__ for %s" % name
    for cls in self.__class__.__mro__ + (self, ):
        if name in cls.__dict__:
            return object.__setattr__(self, name, value)
    print 'New name', name, value
    self.new_names[name] = value

クラス、基本クラス、またはインスタンスに名前がすでに定義されているかどうかをチェックし、記述子 __ set __ を実行する object .__ setattr __ を呼び出します。

別の方法:

def __setattr__(self, name, value):
    print "MyObj.__setattr__ for %s" % name
    try:
        object.__getattribute__(self, name)
    except AttributeError:
        print 'New name', name, value
        self.new_names[name] = value
    else:
        object.__setattr__(self, name, value)

しかし、それは記述子の __ get __ を呼び出すでしょう。

P.S.

MyObj には、 __ dict に継承されたクラスメンバーが含まれているので、すべての __ mro __ のメンバーを確認する必要はありません。

おそらく(self .__ class__、self)のcls:... で十分でしょう。

4
追加された
あなたは2番目の方法は動作しません。私の例では、まだ存在していないオブジェクトに対して新しい属性を設定することは許されていますが、この方法では許可されません。
追加された 著者 SpoonMeiser,
o.new_names ['quux'] の代わりに o.quux = 3 を実行した場合、 o。dict __ ['quux'] コード>
追加された 著者 SpoonMeiser,
私を無視して、私は間違っています。
追加された 著者 SpoonMeiser,
しかし、問題があります。 __ get __ でAttributeErrorが発生することがある
追加された 著者 SpoonMeiser,
@SpoonMeiser、私はあなたのように動作すると思います。 ideone.com/HMBcc をご覧ください。たぶん私は何かをミスしています。
追加された 著者 reclosedev,
@SpoonMeiser、私は再現できません。あなたのPythonバージョンは何ですか? ideone.com/XXAR5
追加された 著者 reclosedev,
@SpoonMeiser良い点。多分もっと問題があるかもしれません。
追加された 著者 reclosedev,