Как сделать неизменный объект в Python? - PullRequest
159 голосов
/ 28 января 2011

Хотя мне это никогда не было нужно, меня просто поразило, что создание неизменяемого объекта в Python может быть немного сложнее.Вы не можете просто переопределить __setattr__, потому что тогда вы даже не можете установить атрибуты в __init__.Подклассы кортежа - это хитрость, которая работает:

class Immutable(tuple):

    def __new__(cls, a, b):
        return tuple.__new__(cls, (a, b))

    @property
    def a(self):
        return self[0]

    @property
    def b(self):
        return self[1]

    def __str__(self):
        return "<Immutable {0}, {1}>".format(self.a, self.b)

    def __setattr__(self, *ignored):
        raise NotImplementedError

    def __delattr__(self, *ignored):
        raise NotImplementedError

Но тогда у вас есть доступ к переменным a и b через self[0] и self[1], что раздражает.

Возможно ли это в Pure Python?Если нет, то как мне это сделать с расширением C?

(ответы, которые работают только в Python 3, являются приемлемыми).

Обновление:

Таким образом, кортеж подклассов - это способ сделать это в Pure Python, который работает хорошо, за исключением дополнительной возможности доступа к данным с помощью [0], [1] и т. Д. Итак, для завершения этого вопроса все, что не хватает, так это как это сделать«правильно» в C, что, я подозреваю, было бы довольно просто, просто не реализовав geititem или setattribute и т. д. Но вместо того, чтобы делать это самому, я предлагаю вознаграждение за это, потому что я ленивый.:)

Ответы [ 21 ]

0 голосов
/ 01 мая 2015

Альтернативный подход заключается в создании оболочки, которая делает экземпляр неизменным.

class Immutable(object):

    def __init__(self, wrapped):
        super(Immutable, self).__init__()
        object.__setattr__(self, '_wrapped', wrapped)

    def __getattribute__(self, item):
        return object.__getattribute__(self, '_wrapped').__getattribute__(item)

    def __setattr__(self, key, value):
        raise ImmutableError('Object {0} is immutable.'.format(self._wrapped))

    __delattr__ = __setattr__

    def __iter__(self):
        return object.__getattribute__(self, '_wrapped').__iter__()

    def next(self):
        return object.__getattribute__(self, '_wrapped').next()

    def __getitem__(self, item):
        return object.__getattribute__(self, '_wrapped').__getitem__(item)

immutable_instance = Immutable(my_instance)

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

Может также использоваться на неизменных фабриках, таких как:

@classmethod
def immutable_factory(cls, *args, **kwargs):
    return Immutable(cls.__init__(*args, **kwargs))

Также защищает от object.__setattr__, но допускает другие трюки из-за динамической природы Python.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...