允许 super 在子类上控制 __setattr__

Allow super to control __setattr__ on subclasses

这个问题是关于基于 super() 和 if/how super can/should control __setattr__ on sub[=104= 的对象的只读问题]es.

上下文:

有没有一种方法可以编写 meta classdescriptor 以便所有 [=作为包含属性 self.read_only = True 的 class 子class 的 104=]es 无法执行 getattr 以 "set_" 开头的子classed 函数,但是self.read_only = False 哪里可以?

我认为 object.__setattr__(self, name, value) 的覆盖:

Called when an attribute assignment is attempted. This is called instead of the normal mechanism (i.e. store the value in the instance dictionary). name is the attribute name, value is the value to be assigned to it.

...是正确的方向,但我怀疑我对文档的解释是否正确。

示例:

超级 系统设计者的意图:

class BaseMessage(object):
    def __init__(self, sender, receiver, read_only=True):
        self.sender = sender
        self.receiver = receiver
        self.read_only = read_only

    def __setattr__(self, name, value):
        if self.read_only:
            raise AttributeError("Can't set attributes as message is read only.")
        else:
            # ? set attribute...(suggestion welcome)

    def get_sender(self):  # Is a "get" and not "set" call, so it should be callable disregarding self.read_only's value.
        return self.sender

    def get_receiver(self):
        return self.receiver

Subsystem-extender 制作,他对所有后果的理解有限:

class MutableMessage(BaseMessage):
    def __init__(self, sender, receiver, topic, read_only=False):
        super().__init__(sender=sender, receiver=receiver, read_only=read_only)
        self.topic = topic

    # this call should be okay as super's property is read_only=False.
    def set_topic_after_init(new_topic):
        self.topic = topic

class ImmutableMessage(BaseMessage):
    def __init__(self, sender, receiver, topic):  # read_only=True !
        super().__init__(sender=sender, receiver=receiver, read_only=read_only)
        self.topic = topic

    # this call should fail as super's property is read_only=True.
    def set_topic_after_init(new_topic):  
        self.topic = topic

示例注释

MutableMessage 中,系统扩展程序明确声明 read_only 为 False,并且知道添加函数 set_topic 的后果。

ImmutableMessage(下)中,系统扩展程序忘记声明消息应该是 read_only=False,这应该导致 supers __setattr__raise AttributeError:

核心问题: 下面示例中所示的用法是否足以一致地应用于所有基于 BaseMessage class 的 classes?

认为我是元编程的新手。因此,对我的示例的任何误解的解释 and/or 扩展和更正将是至高无上的。我了解 hierarchy [1] 但不了解 python 在继承过程中在幕后做了什么。

谢谢...

[1]:层次结构

The search order that Python uses for attributes goes like this:

  1. __getattribute__ and __setattr__
  2. Data descriptors, like property
  3. Instance variables from the object's __dict__
  4. Non-Data descriptors (like methods) and other class variables
  5. __getattr__

Since __setattr__ is first in line, if you have one you need to make it smart unless want it to handle all attribute setting for your class. It can be smart in either of two ways.

a. Make it handle a specific set attributes only, or,

b. make it handle all but some set of attributes.

For the ones you don't want it to handle, call super().__setattr__.

相关问题:

这有点管用:

class BaseMessage(object):
    def __init__(self, sender, receiver, read_only=True):
        self._sender = sender
        self._receiver = receiver
        self.read_only = read_only

    def __setattr__(self, name, value):
        # Have to use gettattr - read_only may not be defined yet.
        if getattr(self,"read_only",False):
            raise AttributeError("Can't set attributes as message is read only.")
        else:
            self.__dict__[name] = value

    def get_sender(self):    # Is a "get" and not "set" call, so it should be callable 
        return self._sender  # disregarding self.read_only's value.

    def get_receiver(self):
        return self._receiver


class ImmutableMessage(BaseMessage):
    def __init__(self, sender, receiver, topic):  # read_only=True !
        # Have to make it read-write to start with
        super().__init__(sender=sender, receiver=receiver, read_only=False)
        # ... so we can set the topic
        self.topic = topic
        # Now make it read-only
        self.read_only = True

    # this call should fail as super's property is read_only=True.
    def set_topic_after_init(self,new_topic):  
        self.topic = new_topic
  • 设置属性很容易(只需设置 __dict__)。
  • 显然 none 这将阻止有人考虑覆盖 __setattr__
  • 最大的缺点是您必须将子类创建为可读写,以便您可以首先创建属性,然后将其标记为只读。我想不出一种方法来自动化。
    • 您可能能够使 super.__init__ 调用派生 __init__ 中的最后一个调用 - 但这感觉不太自然。
    • 或者,__setattr__ 可以遍历堆栈并发现它是否被从 __init__ 调用——但这感觉很麻烦。