我有一个使用__slots__
的类,并且通过覆盖__setattr__
使它们几乎不可变,总是引发错误:
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
是一个占位符,用于与某些自定义硬件交互的复杂操作。
由于x
是属性,我希望能够执行类似
inst = A(1, 2)
inst.x = 3
相反,我看到AttributeError
的消息为Immutable!
。
此处有许多明显的解决方法,例如删除自定义__setattr__
(我不想这样做)或将其重写为
def __setattr__(self, attr, value):
if attr != 'x':
raise AttributeError('Immutable!')
super().__setattr__(attr, value)
这似乎是一种尴尬的方法,如果我开始添加更多这样的属性,它可能会不成比例地膨胀。
真正的问题是我不明白为什么__slots__
与财产之间没有冲突,但__setattr__
与财产之间存在冲突。查找顺序发生了什么,是否存在另一个更优雅的解决方法?
答案 0 :(得分:3)
真正的问题是,我不明白为什么
__slots__
和属性之间没有冲突,但__setattr__
和属性之间存在冲突。
__slots__
和property
都通过为相应的属性提供descriptor来实现属性查找。 __slots__
的存在可防止任意实例属性创建,而不是通过对__setattr__
执行任何操作,而是通过阻止创建__dict__
。 property
和其他描述符不依赖于实例__dict__
,因此它们不受影响。
但是,__setattr__
处理所有属性赋值,这意味着描述符调用是__setattr__
的责任。如果__setattr__
不处理描述符,则不会处理描述符,也不会调用property
setter。
这个问题还有另一种更优雅的解决方法吗?
您可以明确仅允许属性:
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__
:
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)