在Python中使类不可变的方法
我正在进行一些分布式计算,几台机器之间需要沟通,而它们都假设自己拥有相同版本的各种类。因此,设计这些类为不可变的似乎是个不错的主意;这并不是说要防止有坏意图的用户,而是要确保这些类不会被意外修改。
我该怎么做呢?比如,我想实现一个元类,让使用它的类在定义后变得不可变。
>>> class A(object):
... __metaclass__ = ImmutableMetaclass
>>> A.something = SomethingElse # Don't want this
>>> a = A()
>>> a.something = Whatever # obviously, this is still perfectly fine.
其他方法也可以,比如一个装饰器或函数,接收一个类并返回一个不可变的类。
4 个回答
1
如果你不介意使用别人的作品:
http://packages.python.org/pysistence/
这是一些不可变的持久数据结构(这里的“持久”是指功能上的,而不是写入磁盘的意思)。
即使你不直接使用它们,源代码也应该能给你一些灵感。例如,他们的扩展类(expando class)在构造时接收一个对象,然后返回这个对象的不可变版本。
7
别浪费时间在不可变类上。
其实,有很多事情比搞定不可变对象简单得多。
这里有五种不同的方法,你可以根据需要选择其中的任何一种,或者组合使用它们。任何一种方法都能奏效。
文档。其实,他们不会忘记这些的,给他们点信用。
单元测试。用一个简单的模拟对象来模拟你的应用对象,处理
__setattr__
时抛出异常。对象状态的任何变化在单元测试中都是失败。这很简单,不需要复杂的编程。重写
__setattr__
方法,让它在每次尝试写入时抛出异常。collections.namedtuple
。这个是开箱即用的不可变对象。collections.Mapping
。这个也是不可变的,但你需要实现一些方法才能让它工作。
10
如果你觉得使用 __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