使用自定义 __setattr__ 和 __slots__ 进行 Python 属性查找 [英] Python property lookup with custom __setattr__ and __slots__
问题描述
我有一个使用 __slots__
的类,并通过覆盖 __setattr__
使它们几乎不可变,以始终引发错误:
I have a class that uses __slots__
and makes them nearly immutable by overriding __setattr__
to always raise an error:
class A:
__slots__ = ['a', 'b', '_x']
def __init__(self, a, b):
object.__setattr__(self, 'a', a)
object.__setattr__(self, 'b', b)
def __setattr__(self, attr, value):
raise AttributeError('Immutable!')
@property
def x():
return self._x
@x.setter
def x(value):
object.__setattr__(self, '_x', value)
此处,私有"属性 _x
是与某些自定义硬件交互的复杂操作的占位符.
Here, the "private" attribute _x
is a place-holder for a complex operation to interact with some custom hardware.
由于 x
是一个属性,我希望能够做类似的事情
Since x
is a property, I expect to be able to do something like
inst = A(1, 2)
inst.x = 3
相反,我看到了带有消息 Immutable!
的 AttributeError
.
Instead, I see my AttributeError
with the message Immutable!
.
这里有许多明显的解决方法,例如删除自定义 __setattr__
(我不想这样做)或将其重写为
There are a number of obvious workarounds here, such as to remove the custom __setattr__
(which I do not want to do) or to rewrite it as
def __setattr__(self, attr, value):
if attr != 'x':
raise AttributeError('Immutable!')
super().__setattr__(attr, value)
这似乎是一种笨拙的方法,如果我开始添加更多类似的属性,它有可能不成比例地膨胀.
This seems like an awkward method that has the potential to balloon out of proportion if I start adding more properties like that.
真正的问题是我不明白为什么 __slots__
和属性之间没有冲突,但是 __setattr__
和属性之间存在冲突.查找顺序发生了什么变化,是否还有其他更优雅的解决方法来解决此问题?
The real issue is that I do not understand why there is no conflict between __slots__
and the property, but there is one between __setattr__
and the property. What is happening with the lookup order, and is there another, more elegant workaround to this problem?
推荐答案
真正的问题是我不明白为什么
__slots__
和属性之间没有冲突,但是__setattr__
和属性之间存在冲突.
The real issue is that I do not understand why there is no conflict between
__slots__
and the property, but there is one between__setattr__
and the property.
__slots__
和 property
都通过提供 描述符.__slots__
的存在阻止了任意实例属性的创建,不是通过对 __setattr__
做任何事情,而是通过阻止创建 __dict__
.property
和其他描述符不依赖于实例 __dict__
,因此它们不受影响.
Both __slots__
and property
implement attribute lookup by providing a descriptor for the corresponding attribute(s). The presence of __slots__
prevents arbitrary instance attribute creation not by doing anything to __setattr__
, but by preventing creation of a __dict__
. property
and other descriptors don't rely on an instance __dict__
, so they're unaffected.
然而,__setattr__
处理所有属性分配,这意味着描述符调用是__setattr__
的责任.如果您的 __setattr__
不处理描述符,则不会处理描述符,并且不会调用 property
设置器.
However, __setattr__
handles all attribute assignment, meaning that descriptor invocation is __setattr__
's responsibility. If your __setattr__
doesn't handle descriptors, descriptors won't be handled, and property
setters won't be invoked.
有没有其他更优雅的解决方法来解决这个问题?
is there another, more elegant workaround to this problem?
您可以明确只允许属性:
You could explicitly allow only properties:
class A:
...
def __setattr__(self, name, value):
if not isinstance(getattr(type(self), name, None), property):
raise AttributeError("Can't assign to attribute " + name)
super().__setattr__(name, value)
或者您可以明确拒绝分配给槽,并将其他属性分配委托给 super().__setattr__
:
or you could explicitly reject assignment to slots, and delegate other attribute assignment to super().__setattr__
:
class A:
...
def __setattr__(self, name, value):
if isinstance(getattr(type(self), name, None), _SlotDescriptorType):
raise AttributeError("Can't assign to slot " + name)
super().__setattr__(name, value)
# Seems to be the same as types.MemberDescriptorType,
# but the docs don't guarantee it.
_SlotDescriptorType = type(A.a)
这篇关于使用自定义 __setattr__ 和 __slots__ 进行 Python 属性查找的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!