Как сделать класс неизменным в python?

Я много читал об этом предмете, но я все еще не могу найти подходящий ответ. У меня есть класс вроде:

class A(object):

    def __init__(self, first, second):
        self.first = first
        self.second = second

    def __eq__(self, other):
        return ****

    def __str__(self):
        return *****

    def __repr__(self):
        return **** 

a = A("a", "b")

Как я могу запретить a.first = "c", например?

Ответ 1

Вы можете переопределить __setattr__, чтобы предотвратить любые изменения:

def __setattr__(self, name, value):
    raise AttributeError('''Can't set attribute "{0}"'''.format(name))

или запретить добавлять новые атрибуты:

def __setattr__(self, name, value):
    if not hasattr(self, name):
        raise AttributeError('''Can't set attribute "{0}"'''.format(name))
    # Or whatever the base class is, if not object.
    # You can use super(), if appropriate.
    object.__setattr__(self, name, value)

Вы также можете заменить hasattr на проверку на список разрешенных атрибутов:

if name not in list_of_allowed_attributes_to_change:
    raise AttributeError('''Can't set attribute "{0}"'''.format(name))

Другой подход заключается в использовании свойств вместо простых атрибутов:

class A(object):

    def __init__(self, first, second):
        self._first = first
        self._second = second

    @property
    def first(self):
        return self._first

    @property
    def second(self):
        return self._second

Ответ 2

Вы можете отключить __setattr__ как последний шаг инициализации объекта.

class A(object):

    def __init__(self, first, second):
        self.first = first
        self.second = second
        self.frozen = True

    def __setattr__(self, name, value):
        if getattr(self, 'frozen', False):
            raise AttributeError('Attempt to modify immutable object')
        super(A, self).__setattr__(name, value)

>>> a = A(1, 2)
>>> a.first, a.second
(1, 2)
>>> a.first = 3

Traceback (most recent call last):
  File "<pyshell#46>", line 1, in <module>
    a.first = 3
  File "<pyshell#41>", line 10, in __setattr__
    raise AttributeError('Attempt to modify immutable object')
AttributeError: Attempt to modify immutable object

Изменить: В этом ответе есть недостаток, который, я уверен, разделяется всеми другими решениями: если сами члены изменяются, ничто не защищает их. Если ваш объект содержит список, например, все это. Это противоречит, например, С++, где объявление объекта const рекурсивно распространяется на все его элементы.