例如,我有一个带有字段__x
的类,它是一个列表:
class C():
def __init__(self, xx):
self.__x = xx
@property
def x(self):
return self.__x
@x.setter
def x(self, xx):
raise Exception("Attempt to change an immutable field")
我可以防止此类更改:
c = C([1,2,3])
c.x = [3,2,1]
但是如何防止这种变化?
c.x.append(4)
答案 0 :(得分:2)
归根结底,您不能保护自己的对象不受检查和操纵。 另外,请始终问自己“到底是谁?”当您要“保护”数据时。
有时候,为不阅读文档的用户编写代码是不值得的。
话虽如此,您可以在吸气剂中考虑return tuple(self.__x)
。
另一方面,如果__x
包含其他可变对象,则不会阻止用户操纵这些内部对象。 (return list(self.__x)
还将返回数据的浅表副本,但隐含的“嘿,我应该是不可变的!”信号较少。)
您绝对应该考虑的是将self.__x = xx
方法中的self.__x = list(xx)
更改为__init__
,以便用户这样做
var = []
c = C(var)
无法通过突变c
来“轻松”(或错误地又可能存在可变的内部对象)更改var
的状态。
答案 1 :(得分:0)
最简单的方法是在__init__
上接受一个iterable并将其内部转换为元组:
class C(object):
def __init__(self, iterable):
self._tuple = tuple(iterable)
@property
def x(self):
return self._tuple
@x.setter
def x(self, value):
raise RuntimeError('can\'t reset the x attribute.')
c = C([1, 2, 3])
# c.x = 'a' Will raise 'RuntimeError: can't reset the x attribute.'
print(c.x)
像这样的设计使从该类实例化的任何对象都是不可变的,因此,变异操作应返回新对象,而不是更改当前对象的状态。
例如,假设您要实现一个功能,该功能将self.x
中的每个项目增加一个。使用这种方法,您需要编写类似于以下内容的文件:
def increment_by_one(c):
return C(t+1 for t in c.x)
由于创建和销毁对象会产生成本,因此应在您的用例中评估此方法(可防止x
属性发生突变)与@timgeb建议的方法之间的权衡。 / p>