具有属性构建的不可变类

时间:2017-06-16 03:57:24

标签: python oop immutability

我有一个班级DifferentialExtension

class DifferentialExtension(object):
    __slots__ = ('f', 'x', 'D', 'T')
    def __init__(self, f=None, x=None):
        /*
        # some code that builds up list 'self.D'
        */
        self.D = tuple(self.D)
        return None

我要使类“不可变”,即不允许使用DifferentialExtension创建的对象更改属性“D”(在__init__完成后),这些属性都不会被分配给一个新的对象。 D不一定是list,最后返回时可以是tuple

In [1]: DE = DifferentialExtension(log(x), x)
In [2]: DE.D
Out[2]: ((Poly(1, x, domain='ZZ'), Poly(1/x, t0, domain='ZZ(x)'))
In [3]: DE.D = (1, 5, 5)  # raises Error.

3 个答案:

答案 0 :(得分:3)

一般来说,在Python中,你必须假设使用你的课程的人并没有试图做一些恶毒的事情。因此,如果他真的想要改变D的值,他可能有充分的理由。因此,您可能不希望无法更改D,因为这不是pythonic。但是,您可能希望帮助用户不要意外更改D。最好使用只读属性,如:

class DifferentialExtension(object):
    def __init__(self, f=None, x=None):
        self._D = 'immutable value'
        return None

    @property
    def D(self):
        return self._D

运行此命令:

>>> d = DifferentialExtension()
>>> d.D = 'mutate'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: can't set attribute

如果用户确实想要更改该值,他可以直接访问self._D但是你必须假设他知道自己在做什么,因为他正在弄乱一个您的内部强调变量。

答案 1 :(得分:1)

这样的东西?

 class DifferentialExtension(object):
     _frozen = set()
     __slots__ = ('f', 'x', 'D', 'T')
     def __init__(self, f=None, x=None):
         self.D = 'something'
         self.D = tuple(self.D)
         self._frozen.add(id(self))

     def __setattr__(self, attr, value):
         if id(self) in self._frozen:
            raise TypeError('object is frozen')
         object.__setattr__(self, attr, value)

<强>测试

In [29]: a = DifferentialExtension('eh', 'oh')

In [30]: a.D
Out[30]: ('s', 'o', 'm', 'e', 't', 'h', 'i', 'n', 'g')

In [31]: a.D = 'something else'
...
TypeError: object is frozen

编辑。 正如另一个答案中所提到的,命名元组是执行此操作的自然方式,但由于您在构造期间进行了一些计算,因此使用 classmethod 作为替代构造函数:

class DifferentialExtension(namedtuple('DifferentialExtension', 'f, x, D, T')):
    @classmethod
    def build_me(cls, f=None, x=None):
        # a bunch of code that works on D and sets D and T (if you need T)
        T = 'something T'
        D = 'something' ##D = tuple(D) works
        return cls(f, x, D, T)

测试namedtuple

In [41]: DE = DifferentialExtension.build_me(f='some f value', x='some x value')

In [42]: DE.D
Out[42]: 'something'

In [43]: DE.D = 'some other thing'
...

AttributeError: can't set attribute

答案 2 :(得分:1)

使用namedtuple作为不可变对象的基础。如果您愿意,可以根据自己的需要扩展它们。

from collections import namedtuple

class DifferentialExtension(namedtuple('DifferentialExtension', 'f x')):
    def another_method(self):
        print self.x

x = DifferentialExtension(1, 2)

x.f = 2.2
# AttributeError: can't set attribute
相关问题