在观看Raymond Hettinger在PyCon 2018上的演讲时,Dataclasses:结束所有代码生成器的代码生成器-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)
是否可以在没有数据类的情况下复制在setattr
和delattr
内部调用super()
的行为来模拟不变性?
正如@juanpa.arrivilaga在一条评论中指出的那样,可以使用内置的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.