如何防止Python中的类字段(列表)更改?

时间:2018-11-21 22:04:46

标签: python

例如,我有一个带有字段__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)

2 个答案:

答案 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>