使用自定义__setattr__和__slots__进行Python属性查找

时间:2017-09-07 16:19:11

标签: python python-3.x properties

我有一个使用__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__与财产之间存在冲突。查找顺序发生了什么,是否存在另一个更优雅的解决方法?

1 个答案:

答案 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)