从数据类复制 setattr 和 delattr 的行为

Replicating the behaviour of setattr and delattr from dataclasses

在观看 Raymond Hettinger 在 PyCon 2018 上的演讲时,Dataclasses: The code generator to end all code generators - PyCon 2018提供了数据类如何在冻结数据类上实现 setattr 和 delattr 的示例:

def __setattr__(self, name, value):
    if type(self) is cls or name in ('name', 'address', 'zip_code', 'account_balance', 'active_orders'):
        raise FrozenInstanceError(f'Cannot assign to field {name!r}')
    super(cls, self).__setattr__(name, value)

def __delattr__(self, name):
    if type(self) is cls or name in ('name', 'address', 'zip_code', 'account_balance', 'active_orders'):
        raise FrozenInstanceError(f'Cannot delete field {name!r}')
    super(cls, self).__delattr__(name)

是否可以在 setattrdelattr 中复制调用 super() 的行为,而无需数据类来模拟不变性?

正如 @juanpa.arrivillaga 在评论中指出的那样,您 可以 使用内置 super() 函数复制行为,但是您 不需要 向它传递任何参数(至少在Python 3 中)。 示例:

class Class:
    class FrozenInstanceError(Exception): pass

    fieldnames = 'name', 'address', 'zip_code', 'account_balance', 'active_orders'

    def __init__(self):
        super().__setattr__('account_balance', 0)  # Bypasses this class' __setattr__().

    def __setattr__(self, name, value):
        if isinstance(self, Class) and name in self.fieldnames:
            raise self.FrozenInstanceError(f'Cannot assign to field {name!r}')
        super().__setattr__(name, value)

    def __delattr__(self, name):
        if isinstance(self, Class) and name in self.fieldnames:
            raise self.FrozenInstanceError(f'Cannot delete field {name!r}')
        super().__delattr__(name)


if __name__ == '__main__':

    inst = Class()
    print(inst.account_balance)  # -> 0
    try:
        inst.account_balance = 1000000
    except Exception as exc:
        print(exc)  # -> Cannot assign to field 'account_balance'
    else:
        print('Error: Somehow assigned value to "account_balance" field')

    try:
        inst.something_else = 42  # OK, not a field name.
    except Exception as exc:
        print(exc)  # -> 'Error: Wasn't allowed to create "something_else" field'
    else:
        print('Successfully created "something_else" attribute.')  # Prints.

    try:
        del inst.account_balance
    except Exception as exc:
        print(exc)  # -> Cannot delete field 'account_balance'
    else:
        print('Error: Somehow deleted protected "account_balance" field')

    try:
        del inst.something_else
    except Exception as exc:
        print(exc)  # -> 42
    else:
        print('Successfully deleted "something_else" attribute.')  # Prints.