我不认为这是完全可能的,除非使用一个元组或namedtuple。无论如何,如果你重写了__setattr__(),用户总是可以通过直接调用object.__setattr__()来绕过它。任何依赖__setattr__的解决方案都保证不起作用。
以下是不使用某种元组可以得到的最接近的结果:
class Immutable:
__slots__ = ['a', 'b']
def __init__(self, a, b):
object.__setattr__(self, 'a', a)
object.__setattr__(self, 'b', b)
def __setattr__(self, *ignored):
raise NotImplementedError
__delattr__ = __setattr__
但如果你足够努力,它就会破裂:
>>> t = Immutable(1, 2)
>>> t.a
1
>>> object.__setattr__(t, 'a', 2)
>>> t.a
2
但Sven对namedtuple的使用确实是不可变的。
更新
由于这个问题已经更新为询问如何在C中正确地做这件事,下面是我关于如何在Cython中正确地做这件事的答案:
第一个immutable.pyx:
cdef class Immutable:
cdef object _a, _b
def __init__(self, a, b):
self._a = a
self._b = b
property a:
def __get__(self):
return self._a
property b:
def __get__(self):
return self._b
def __repr__(self):
return "<Immutable {0}, {1}>".format(self.a, self.b)
和一个setup.py来编译它(使用命令setup.py build_ext——inplace:
from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext
ext_modules = [Extension("immutable", ["immutable.pyx"])]
setup(
name = 'Immutable object',
cmdclass = {'build_ext': build_ext},
ext_modules = ext_modules
)
然后试试吧:
>>> from immutable import Immutable
>>> p = Immutable(2, 3)
>>> p
<Immutable 2, 3>
>>> p.a = 1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: attribute 'a' of 'immutable.Immutable' objects is not writable
>>> object.__setattr__(p, 'a', 1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: attribute 'a' of 'immutable.Immutable' objects is not writable
>>> p.a, p.b
(2, 3)
>>>