从数据类复制 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)
是否可以在 setattr
和 delattr
中复制调用 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.
在观看 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)
是否可以在 setattr
和 delattr
中复制调用 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.