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

Mad*_*ist 3 python properties python-3.x

我有一个类,它使用__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)
Run Code Online (Sandbox Code Playgroud)

在这里,“私有”属性_x是与某些自定义硬件交互的复杂操作的占位符。

由于x是财产,我希望能够做类似的事情

 inst = A(1, 2)
 inst.x = 3
Run Code Online (Sandbox Code Playgroud)

相反,我看到了我的AttributeError消息Immutable!。

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

def __setattr__(self, attr, value):
    if attr != 'x':
        raise AttributeError('Immutable!')
    super().__setattr__(attr, value)
Run Code Online (Sandbox Code Playgroud)

这似乎是一种尴尬的方法,如果我开始添加更多类似的属性,就有可能变得不成比例。

__slots__真正的问题是,我不明白为什么和财产之间没有冲突,但__setattr__和财产之间却有冲突。查找顺序发生了什么,是否有另一种更优雅的解决方法来解决这个问题?

use*_*ica 5

__slots__真正的问题是,我不明白为什么和财产之间没有冲突,但__setattr__和财产之间却有冲突。

__slots__和两者都通过提供相应属性的描述符property来实现属性查找。的存在阻止了任意实例属性的创建,不是通过对 执行任何操作,而是通过阻止创建. 而其他描述符不依赖于实例,因此它们不受影响。__slots____setattr____dict__property__dict__

但是,__setattr__处理所有属性分配,这意味着描述符调用是__setattr__的责任。如果您__setattr__不处理描述符,则不会处理描述符,并且property不会调用设置器。

对于这个问题还有另一种更优雅的解决方法吗?

您可以明确仅允许属性:

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)
Run Code Online (Sandbox Code Playgroud)

或者您可以明确拒绝对插槽的分配,并将其他属性分配委托给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)
Run Code Online (Sandbox Code Playgroud)