如何在Python中使类不可变

por*_*uod 16 python metaclass immutability

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

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

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

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

S.L*_*ott 7

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

您可以做的一些事情比尝试创建不可变对象要简单得多。

这里有五种单独的技术。您可以从其中挑选。任何一个都可以工作。有些组合也能发挥作用。

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

  2. 单元测试。__setattr__使用作为异常处理的简单模拟来模拟您的应用程序对象。对对象状态的任何更改都会导致单元测试失败。它很简单,不需要任何复杂的编程。

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

  4. collections.namedtuple。它们开箱即用,是不可变的。

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

  • @S.Lott:嗯,我想你可以将我的问题解释为“我怎样才能创建一个不可变的类,或者,谁能告诉我为什么我不应该这样做?”。也许我会更幸运“如果我们理所当然地认为我实际上有充分的理由这样做,我怎样才能创建一个不可变的类。” 我可能得不到任何答案,但我会接受任何一天有人反复告诉我我不知道我想要什么,即使在我明确要求手头实际问题的答案之后也是如此。 (8认同)
  • 对于学习 Python 的数据模型并练习不可变类非常有启发性。理解为什么`object.__setattr__`或`type.__setattr__`仍然可以绕过大多数自制尝试是一个很好的学习经历。遗憾的是,您选择听问题的方式,其中有人说“我想做 X”,而您只是回答“停止想做 X”。想要做 X 可能非常有用,如果只是为了教育学,并且答案空间并不是您对问题前提的个人规范判断的好位置。发表评论。或者,更好的是,忽略它。 (4认同)
  • @porgarmingduod:这是一个答案,而不是更多评论。它是孤立的。其他有同样问题的人不会继续评论。没有“更可靠的方法”。这是对大脑卡路里的浪费。程序员并不愚蠢、健忘、容易发生事故或反社会。构建您的应用程序。让它发挥作用。提出例外情况。这就是 Pythonic 的方式。 (2认同)
  • @S.Lott:我在哪里要求你为我做任何事?暗示我对你有任何要求是很不合理的。你不可能在我所说的任何内容中找到任何依据来提出这一主张。我很早就承认我正在尝试做的事情可能不是一个好主意。我什至不想捍卫这一点。我只是试图捍卫实际的问题,这个问题非常具体并且可以回答。您在最初的评论中提出了“这可能是一个愚蠢的想法”的观点。另外,感谢您将自己的观点称为“我们”。你是Python社区的共识吗? (2认同)

jsb*_*eno 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
Run Code Online (Sandbox Code Playgroud)