Способы сделать класс неизменным в Python - PullRequest
16 голосов
/ 14 февраля 2011

Я делаю некоторые распределенные вычисления, в которых несколько машин взаимодействуют, предполагая, что все они имеют идентичные версии различных классов. Таким образом, кажется хорошим дизайном сделать эти классы неизменяемыми; не в том смысле, что он должен мешать пользователю с плохими намерениями, просто достаточно неизменным, чтобы он никогда не был изменен случайно.

Как бы я поступил по этому поводу? Например, как мне реализовать метакласс, который делает класс, использующий его, неизменным после его определения?

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

Альтернативные методы также хороши, такие как декоратор / функция, которая принимает класс и возвращает неизменный класс.

Ответы [ 3 ]

6 голосов
/ 15 февраля 2011

Если старая уловка использования __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
4 голосов
/ 15 февраля 2011

Не тратьте время на неизменяемые классы.

Есть вещи, которые вы можете сделать намного проще, чем возиться с попыткой создать неизменный объект.

Вот пятьотдельные техники.Вы можете выбирать из них.Любой будет работать.Также подойдут некоторые комбинации.

  1. Документация.На самом деле, они не забудут это.Дайте им кредит.

  2. Юнит-тест.Смоделируйте объекты вашего приложения с помощью простого макета, который обрабатывает __setattr__ в качестве исключения.Любое изменение состояния объекта является ошибкой в ​​модульном тесте.Это просто и не требует какого-либо сложного программирования.

  3. Переопределить __setattr__, чтобы вызвать исключение при каждой попытке записи.

  4. collections.namedtuple.Они неизменны из коробки.

  5. collections.Mapping.Он неизменен, но вам нужно реализовать несколько методов, чтобы он работал.

1 голос
/ 14 февраля 2011

Если вы не возражаете против повторного использования чужой работы:

http://packages.python.org/pysistence/

Неизменяемые постоянные (в функциональном смысле, а не в виде записи) структуры данных.* Даже если вы не используете их как есть, исходный код должен вдохновлять вас.Например, их класс expando принимает объект в своем конструкторе и возвращает его неизменную версию.

...