【问题标题】:__setattr__ method to ensure objects in the class are immutable__setattr__ 方法来确保类中的对象是不可变的
【发布时间】:2014-04-26 06:20:49
【问题描述】:

我定义了以下类(其中很大一部分缩短了),但我的主要问题是,一旦使用其初始化变量定义了此类的对象,我想确保这些变量在类外部是不可更改的...如果进行了这样的尝试,我将需要引发 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)

【问题讨论】:

标签: python immutability setattr


【解决方案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__ ...


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

【讨论】:

  • 我记得,如果你曾经定义过 slots = (),你将无法在子类化上添加字段
  • @akaRem -- 你可以(试试看:)。子类不继承父类的__slots__。
【解决方案2】:

您的__setattr__ 也会阻止对象设置自己的属性,例如您在__init__ 中设置的self.min 和self.max。

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

如果您只需要初始化其中的几个属性,则使用property 并为每个属性定义一个仅限 getter 的属性可能更具可读性。但是,与使用 __setattr__ 不同,这不会阻止用户向您的类添加新属性。

【讨论】:

  • 所以对于我的 init 方法中的两个属性将它们添加到 self.__dict__ ?我实际上是通过将我的它们添加到 (self.__dict__['min'] = min self.__dict__['max'] = max) 并在我的 setattr 方法中检查是否传入的值它不在这本字典中,然后引发 AssertionError
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-11-09
  • 1970-01-01
  • 2017-09-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多