【问题标题】:Python property lookup with custom __setattr__ and __slots__使用自定义 __setattr__ 和 __slots__ 查找 Python 属性
【发布时间】:2018-02-16 11:16:33
【问题描述】:

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

【问题讨论】:

  • NVM,看了答案就明白了……
  • @juanpa.arrivillaga。我非常乐意更改您认为我应该更改的任何措辞。
  • 我觉得太早了……

标签: python python-3.x properties


【解决方案1】:

真正的问题是我不明白为什么__slots__和属性之间没有冲突,但__setattr__和属性之间存在冲突。

__slots__property 都通过为相应的属性提供descriptor 来实现属性查找。 __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)

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

【讨论】:

  • 这两个正是我想要的。不过,对于我的特定应用,我更喜欢第一个。
  • 有没有办法在实例上设置属性?即,getattr(type(self), name, None) 在第一种情况下是必要的,还是 getattr(self, name, None) 可以?
  • @MadPhysicist:getattr(self, ... 将获得属性值,而不是处理属性的描述符。 (即使有描述符,getattr(type(self), ... 在技术上也可以返回描述符以外的内容,但 property 和插槽描述符都没有这样做。)
  • 我是否正确假设type(self) 上的描述符在self.__dict__ 之前搜索,所以即使有一个名为x 的属性和一个名为__dict__ 的项目x ,该属性将在self.x = value?中访问
  • @MadPhysicist:只有“数据”描述符——类型实现 __set____delete__ 的描述符——优先于实例字典中的条目。属性和插槽是数据描述符(即使您不提供属性设置器或删除器),因此它们会覆盖实​​例字典,但并非所有描述符都会。 (当然,如果你使用槽,你可能甚至没有实例字典。)
猜你喜欢
  • 2013-12-22
  • 2013-11-03
  • 2020-03-14
  • 1970-01-01
  • 2023-01-23
  • 2023-03-09
  • 2021-05-07
  • 2010-10-23
相关资源
最近更新 更多