<p>有两种广泛的方法:事后验证,或防止突变操作的发生。下面是阻止<code>__setitem__</code>和类似方法被访问的代理类的草图。你知道吗</p>
<pre><code>names = ['__setitem__', 'append', 'pop', 'add', 'remove', 'update']
class immutable_mixin:
def __getattribute__(self, name):
if name in names: raise TypeError
return super().__getattribute__(name)
def __getitem__(self, k): return wrap(super().__getitem__(k))
def __iter__(self): return map(wrap, super().__iter__())
def __repr__(self): return '>>{}<<'.format(super().__repr__())
class immutable_list(immutable_mixin, list): pass
class immutable_set(immutable_mixin, set): pass
class immutable_dict(immutable_mixin, dict): pass
def wrap(x):
if isinstance(x, (int, str, bytes)): return x
elif isinstance(x, list): return immutable_list(x)
elif isinstance(x, set): return immutable_set(x)
elif isinstance(x, dict): return immutable_dict(x)
else: return 'FIXME' + repr(x)
</code></pre>
<p>简言之,变异操作引发<code>TypeError</code>,getter操作确保返回的值是代理的(或者是不能包含其他值的类型)。你知道吗</p>
<pre><code>>>> x = [[1,2,3], {1,2,3}, "other data", 1, {1:1, "2":"2"}]
>>> m = wrap(x)
>>> m
>>[[1, 2, 3], {1, 2, 3}, 'other data', 1, {1: 1, '2': '2'}]<<
>>> list(m)
[>>[1, 2, 3]<<, >>immutable_set({1, 2, 3})<<, 'other data', 1, >>{1: 1, '2': '2'}<<]
</code></pre>
<p>在defaultdict这样的非标准容器面前,它可能很脆弱。它还需要全面的工作,我忘了包括<code>__delitem__</code>和<code>__reversed__</code>,例如,<code>list.extend</code>;集算术还充当转义图案填充(但列表切片没有!)。见<a href="https://docs.python.org/3/reference/datamodel.html" rel="nofollow noreferrer">Python Data Model</a>。列出允许的方法可能比列出不允许的方法更健壮,但是代码会更长。你知道吗</p>