如何在Python中使类不可变

时间:2011-02-14 20:16:37

标签: python immutability metaclass

我正在做一些分布式计算,其中几台机器在假设它们都具有相同版本的各种类的情况下进行通信。因此,使这些类不可变是一种好的设计;不是因为它必须挫败一个有着不良意图的用户,只是一成不变,永远不会被意外修改。

我该怎么做?例如,我如何实现一个元类,使得类在定义后使用它是不可变的?

>>> class A(object):
...     __metaclass__ = ImmutableMetaclass
>>> A.something = SomethingElse # Don't want this
>>> a = A()
>>> a.something = Whatever # obviously, this is still perfectly fine.

替代方法也很好,例如一个装饰器/函数,它接受一个类并返回一个不可变的类。

3 个答案:

答案 0 :(得分:6)

如果使用__slots__的旧技巧不适合你,这个或其中的一些变体可以做到: 只需编写元类的__setattr__方法即可成为您的守护者。在此示例中,我阻止了分配的新属性,但允许修改现有属性:

def immutable_meta(name, bases, dct):
    class Meta(type):
        def __init__(cls, name, bases, dct):
            type.__setattr__(cls,"attr",set(dct.keys()))
            type.__init__(cls, name, bases, dct)

        def __setattr__(cls, attr, value):
            if attr not in cls.attr:
                raise AttributeError ("Cannot assign attributes to this class")
            return type.__setattr__(cls, attr, value)
    return Meta(name, bases, dct)


class A:
    __metaclass__ = immutable_meta
    b = "test"

a = A()
a.c = 10 # this works
A.c = 20 # raises valueError

答案 1 :(得分:4)

不要在不可变的课程上浪费时间。

你可以做的事情比试图创建一个不可变对象更简单,更简单。

以下是五种不同的技巧。您可以从中挑选。任何人都会工作。一些组合也可以。

  1. 文档。实际上,他们不会忘记这一点。给予他们信任。

  2. 单元测试。使用处理__setattr__的简单模拟作为例外,模拟您的应用程序对象。对象状态的任何更改都是单元测试失败。它很简单,不需要任何精心编程。

  3. 覆盖__setattr__以在每次尝试写入时引发异常。

  4. collections.namedtuple。它们是开箱即用的。

  5. collections.Mapping。它是不可变的,但你需要实现一些方法才能使它工作。

答案 2 :(得分:1)

如果你不介意重复别人的工作:

http://packages.python.org/pysistence/

不可变持久性(在功能上,而不是写入桌面感)数据结构。

即使您不按原样使用它们,源代码也应该提供一些灵感。例如,他们的expando类在它的构造函数中获取一个对象,并返回它的不可变版本。