对 python 类同时使用 __setattr__ 和描述符
我正在编写一个 python 类,它使用 __setattr__ 和 __getattr__ 来提供自定义属性访问。
但是,某些属性无法以通用方式处理,因此我希望对这些属性使用描述符。
出现的问题是,对于描述符,描述符的 __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
prints:
MyObj.__setattr__ for bar
MyObj.__getattr__ for bar
baz
MyObj.__setattr__ for foo
MyDesc.__get__
None
描述符的 __set__
从未被调用。
由于 __setattr__
定义不仅仅覆盖有限名称集的行为,因此没有明确的地方可以遵循 object.__setattr__
是否有推荐的方法分配给属性时使用描述符(如果可用),否则使用 __setattr__
?
I'm writing a python class that uses __setattr__
and __getattr__
to provide custom attribute access.
However, some attributes can't be handled in a generic way, so I was hoping to use descriptors for those.
A problem arises in that for a descriptor, the descriptor's __get__
will be invoked in favour of the instances __getattr__
, but when assigning to an attribute, __setattr__
will be invoked in favour of the descriptors __set__
.
An example:
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
prints:
MyObj.__setattr__ for bar
MyObj.__getattr__ for bar
baz
MyObj.__setattr__ for foo
MyDesc.__get__
None
The descriptor's __set__
is never called.
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__
Is there a recommended way to have assigning to attributes use the descriptor, if available, and __setattr__
otherwise?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
更新
10多年后重新审视这个答案,我意识到我包含了一个食谱,但没有解释OP遇到的突出行为:在一个descritpor的
__get__
之前被调用实例的 __getattr__ ,是的。但与__setattr__
实际相反的是__getattribute__
,而不是__getattr__
。在object
的__getattribute__
中,首先是属性搜索顺序的代码,以及负责调用描述符的__get__
的代码本身。在检查任何可能的描述符、__slots__
和实例的__dict__ 之后,
。__getattr__
由__getattribute__
本身中的代码调用,作为最后的手段。原始答案:只做OP想要实现的事情
我想我会通过一种机制来自动标记哪些是
每个类中的描述符,并以调用的方式包装
__setattr__
这些名称的对象的正常行为。
这可以通过元类(以及
__setattr__
的装饰器)轻松实现update
Revisiting this answer more than 10 years later, I realized that I included a recipe, but did not explain the outstanding behavior the OP met: a descritpor's
__get__
is called before an instance's__getattr__
, yes. But the actual converse to__setattr__
is__getattribute__
, not__getattr__
. Insideobject
's__getattribute__
is where is the code with the search order for attributes in first place, and the code responsible for calling a descriptor's__get__
itself.__getattr__
is called by the code in__getattribute__
itself as a last resort, after inspecting any possible descriptors,__slots__
, and the instance's__dict__
.original answer: just do what the OP wants to achieve
I think I'd approach this by having a mechanism to automatically mark which are the
descriptors in each class, and wrap the
__setattr__
in a way that it'd callobject's normal behavior for those names.
This can be easily achieved with a metaclass (and a decorator for
__setattr__
可能的方法之一:
它检查名称是否已在类、基类或实例中定义,然后调用
object.__setattr__
,它将执行描述符__set__
。另一种方式:
但它会调用描述符的
__get__
。PS
我不确定是否需要检查所有
__mro__
成员,因为MyObj
将包含__dict__
中继承的类成员。也许
for cls in (self.__class__, self):...
就足够了。One of possible ways:
It checks if name already defined in class, base classes or instance and then it calls
object.__setattr__
which will execute descriptor__set__
.Another way:
But it will call descriptor's
__get__
.P.S.
I'm not sure about need to check all
__mro__
members sinceMyObj
will contain inherited class members in__dict__
.Maybe
for cls in (self.__class__, self):...
will be enough.