__setattr__方法确保类中的对象是不可变的

时间:2014-04-26 06:20:50

标签: python immutability setattr

我定义了以下类(其中很大一部分缩短了)但我的主要问题是,一旦这个类的对象被定义为其初始化变量我想确保这些变量不能从类外部更改...如果进行了这样的尝试,我将需要引发一个AssertionError ..我已经定义了 setattr 方法来引发此异常,但是在将此方法放入其中之后完全破坏了之前的实例和测试每当尝试创建这个类的对象时,这个类一直在使用这个类来提升同样的AssertionError ...所以这是我的类的一个显着简化的版本。

class Interval:
    compare_mode = None 

    def __init__(self, min, max):
        self.min = min 
        self.max = max 

    def min_max(min, max = None)-> 'Interval Object':
    .
    .
    .
    return Interval(min, max)

所有其他类方法......

def __setattr__(self, name, value):

    raise AssertionError('Objects in this class are immutable')

所以一旦我尝试从这个类中测试静音对象,我会在这个类中使用其余的方法破坏以前的测试时出现错误的异常。

p.min = 0;  raised wrong exception(NameError)
p.max = 0;  raised wrong exception(NameError)
p.HeyImChangingYourMethodandThereIsNothingYouCanDoToStopME  raised wrong exception(NameError)

2 个答案:

答案 0 :(得分:1)

您的问题有点难以理解 - 但是,在__init__中,您可以执行以下操作:

self.max = 0

这将调用__setattr__,这将在现场引发您的例外。

如果您真的想要不变性,可以考虑使用collections.namedtuple + __slots__

class Interval(collections.namedtuple('IntervalBase', 'min,max')):
    __slots__ = ()
    def method1(self):
        print self.min
        print self.max

作为演示:

>>> class Interval(collections.namedtuple('IntervalBase', 'min,max')):
...     __slots__ = ()
...     def method1(self):
...         print self.min
...         print self.max
... 
>>> Interval(1,2)
IntervalBase(min=1, max=2)
>>> Interval(1,2).method1
<bound method Interval.method1 of IntervalBase(min=1, max=2)>
>>> Interval(1,2).method1()
1
2
>>> Interval(1,2).foo = 1
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Interval' object has no attribute 'foo'

请注意,用户仍然可以向Interval的子类添加属性,但除非该子类也使用__slots__ ...


另请注意,通常您不应该关心用户是否为您的实例添加了add-hoc属性。如果API提供的方法都不能更改(您可以通过property强制执行),那么对于所有意图和目的,您的类是 immutable ,然后您可以打开不可变性的世界提供(例如合理定义的__hash__)。

答案 1 :(得分:0)

您的__setattr__也会阻止该对象设置自己的属性,例如您在self.min中设置的self.max__init__

如果要避免这种情况,请在__init__末尾设置一个标志,告知对象是否已初始化,并__setattr__检查此标志,如果设置了标志则不允许设置。或者,让__init__通过直接修改self.__dict__(例如self.__dict__['min'] = min)来设置属性。

如果您只有一些这些属性要初始化,那么使用property并为每个属性定义一个getter-only属性可能更具可读性。但是,与使用__setattr__不同,这不会阻止用户向您的类添加新属性。