Как Pythonic способ сделать неизменный класс? - PullRequest
0 голосов
/ 31 мая 2019

В настоящее время я работаю с некоторыми структурами данных в Python, и я хотел бы, чтобы они были хэшируемыми (чтобы их можно было хранить в словарях и наборах).До сих пор я видел три основных способа сделать это:

① Создать нормальный класс и дать ему __hash__ метод.

class DataStructure:
    def __init__(self, member):
        self.member = member
    def __hash__(self):
        return hash(self.member)
    def __eq__(self, other):
        return isinstance(other, DataStructure) and self.member == other.member

Но это просто "доверять"конечный пользователь никогда не мутирует member.Если они изменяют его, пока он хранится в наборе или в словаре, получаются плохие вещи.

② Использовать collections.namedtuple.

DataStructure = collections.namedtuple('DataStructure', ('member',))

Но namedtuple не может иметь функции-членыи было бы неплохо иметь функции-члены.

③ Использовать __slots__ и переопределить __setattr__, как показано здесь .

class DataStructure:
    __slots__ = ['member']
    def __init__(self, member):
        super(DataStructure, self).__setattr__('member', member)
    def __setattr__(self, key, value):
        raise ValueError('Mutating this object is Not Allowed')
    # also define __hash__ and __eq__ here

Но это не похоже на предполагаемое использование , а также усложняет наследование.Прежде всего, он не чувствует себя «Pythonic».

Итак: каков предпочтительный способ создания неизменяемого класса в Python? Желательно, чтобы пользовательская структура данных была хэшируемойкак такое странное желание - вот почему, в конце концов, существует __hash__.И я бы предпочел сделать класс неизменным, а не говорить пользователям: «Вы можете назначать этих участников, но если вы это сделаете, ваши наборы и словари могут зависнуть и сгореть» - Python обычно старается не позволять людям делать это.ошибки в первую очередь (именно поэтому set и frozenset различны).

Ответы [ 2 ]

2 голосов
/ 01 июня 2019

Абсолютная неизменность, вероятно, невозможна, но вы можете использовать свойства , чтобы прояснить свое намерение:

class DataStructure:
    def __init__(self, member):
        self._member = member
    def __hash__(self):
        return hash(self.member)
    def __eq__(self, other):
        return isinstance(other, DataStructure) and self.member == other.member
    @property
    def member(self):
        return self._member

Таким образом member выставляется как "публичный" атрибут, но недоступно для записи:

>>> ds = DataStructure(42)
>>> ds.member
42
>>> ds.member = 56
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: can't set attribute

Определенный конечный пользователь все еще может мутировать self._member, но в этот момент вы ожидаете, что они знают , что они делают не то.

1 голос
/ 31 мая 2019

Добавьте «_» перед переменной, которую вы хотите сделать «только для чтения». Его по-прежнему можно изменить изнутри класса, но IDE не будет его предсказывать и будет выдавать предупреждения, если вы попытаетесь изменить их извне класса.

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