使用自定义的_setattr__和_插槽查找Python属性__

使用自定义的_setattr__和_插槽查找Python属性__,python,python-3.x,properties,Python,Python 3.x,Properties,我有一个类,它使用\uuuuuuu slots\uuuuu并通过重写\uuuuuuu setattr\uuuuuu始终引发错误,使它们几乎不可变: class A: __slots__ = ['a', 'b', '_x'] def __init__(self, a, b): object.__setattr__(self, 'a', a) object.__setattr__(self, 'b', b) def __setattr__(

我有一个类,它使用
\uuuuuuu slots\uuuuu
并通过重写
\uuuuuuu setattr\uuuuuu
始终引发错误,使它们几乎不可变:

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)
这里,“private”属性
\ux
是复杂操作与某些自定义硬件交互的占位符

因为
x
是一个属性,所以我希望能够像

 inst = A(1, 2)
 inst.x = 3
相反,我看到我的
AttributeError
带有消息
不可变

这里有许多明显的解决方法,例如删除自定义的
\uuuuu setattr\uuuuuu
(我不想这样做)或将其重写为

def __setattr__(self, attr, value):
    if attr != 'x':
        raise AttributeError('Immutable!')
    super().__setattr__(attr, value)
这似乎是一个笨拙的方法,如果我开始添加更多这样的属性,它可能会变得不成比例

真正的问题是,我不明白为什么
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu>和属性之间没有冲突,但是
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu。查找顺序发生了什么变化,这个问题是否还有其他更优雅的解决方法

真正的问题是,我不明白为什么
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu>和属性之间没有冲突,但是
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu

属性都通过为相应的属性提供属性来实现属性查找。
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
的存在不是通过对
\uuuuuuuuu setattr\uuuuuuuuuuuuuuuuuuuuuuuu
执行任何操作,而是通过阻止创建
\uuuuuuuuuuuuuuu<代码>属性和其他描述符不依赖实例,因此它们不受影响

但是,
\uuuuuu setattr\uuuuu
处理所有属性赋值,这意味着描述符调用是
\uuuuuuuu setattr\uuuuu
的责任。如果
\uuuu setattr\uuuu
不处理描述符,则不会处理描述符,也不会调用
属性设置器

这个问题还有其他更优雅的解决方法吗

您可以显式地只允许属性:

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()

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)

NVM,我在阅读答案时理解…。。@juanpa.arrivillaga。我非常乐意改变你认为我应该改变的任何措辞。我认为现在太早了……这两个都正是我想要的。不过,对于我的特定应用程序,我更喜欢第一个。有没有办法在实例上设置属性?例如,
getattr(type(self),name,None)
在第一种情况下是必需的,还是
getattr(self,name,None)
OK?@madpysicator:
getattr(self,
将获取属性值,而不是处理属性的描述符。(从技术上讲,
getattr(type(self)是可能的),…
返回描述符以外的内容,即使存在描述符,但
属性
和插槽描述符都不会返回该内容。)我假设
上的描述符类型(self)正确吗
是在
self.\uuuuu dict\uuuuu
之前搜索的,因此,即使有一个名为
x
的属性和一个名为
x
的项目,也会在
self.x=value
中访问该属性。@madpysicator:只有“数据”描述符—其类型实现了
\uuuuu set\uuuuuuuuu
\uuuuuu delete\uuuuuuuuuuuu
的描述符—优先于实例dict中的条目。属性和插槽是数据描述符(即使您没有提供属性设置器或删除器),因此它们会覆盖实例dict,但并非所有描述符都会覆盖实例dict。(当然,如果您使用的是插槽,您甚至可能没有实例dict。)