使用自定义 __setattr__ 和 __slots__ 进行 Python 属性查找 [英] Python property lookup with custom __setattr__ and __slots__

查看:84
本文介绍了使用自定义 __setattr__ 和 __slots__ 进行 Python 属性查找的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个使用 __slots__ 的类,并通过覆盖 __setattr__ 使它们几乎不可变,以始终引发错误:

I have a class that uses __slots__ and makes them nearly immutable by overriding __setattr__ to always raise an error:

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 是与某些自定义硬件交互的复杂操作的占位符.

Here, the "private" attribute _x is a place-holder for a complex operation to interact with some custom hardware.

由于 x 是一个属性,我希望能够做类似的事情

Since x is a property, I expect to be able to do something like

 inst = A(1, 2)
 inst.x = 3

相反,我看到了带有消息 Immutable!AttributeError.

Instead, I see my AttributeError with the message Immutable!.

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

There are a number of obvious workarounds here, such as to remove the custom __setattr__ (which I do not want to do) or to rewrite it as

def __setattr__(self, attr, value):
    if attr != 'x':
        raise AttributeError('Immutable!')
    super().__setattr__(attr, value)

这似乎是一种笨拙的方法,如果我开始添加更多类似的属性,它有可能不成比例地膨胀.

This seems like an awkward method that has the potential to balloon out of proportion if I start adding more properties like that.

真正的问题是我不明白为什么 __slots__ 和属性之间没有冲突,但是 __setattr__ 和属性之间存在冲突.查找顺序发生了什么变化,是否还有其他更优雅的解决方法来解决此问题?

The real issue is that I do not understand why there is no conflict between __slots__ and the property, but there is one between __setattr__ and the property. What is happening with the lookup order, and is there another, more elegant workaround to this problem?

推荐答案

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

The real issue is that I do not understand why there is no conflict between __slots__ and the property, but there is one between __setattr__ and the property.

__slots__property 都通过提供 描述符.__slots__ 的存在阻止了任意实例属性的创建,不是通过对 __setattr__ 做任何事情,而是通过阻止创建 __dict__.property 和其他描述符不依赖于实例 __dict__,因此它们不受影响.

Both __slots__ and property implement attribute lookup by providing a descriptor for the corresponding attribute(s). The presence of __slots__ prevents arbitrary instance attribute creation not by doing anything to __setattr__, but by preventing creation of a __dict__. property and other descriptors don't rely on an instance __dict__, so they're unaffected.

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

However, __setattr__ handles all attribute assignment, meaning that descriptor invocation is __setattr__'s responsibility. If your __setattr__ doesn't handle descriptors, descriptors won't be handled, and property setters won't be invoked.

有没有其他更优雅的解决方法来解决这个问题?

is there another, more elegant workaround to this problem?

您可以明确只允许属性:

You could explicitly allow only properties:

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__:

or you could explicitly reject assignment to slots, and delegate other attribute assignment to 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)

这篇关于使用自定义 __setattr__ 和 __slots__ 进行 Python 属性查找的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆