Python замороженный класс данных, разрешить изменение атрибута методом - PullRequest
1 голос
/ 16 марта 2020

Предположим, у меня есть класс данных:

@dataclass(frozen=True)
class Foo:
    id: str
    name: str

Я хочу, чтобы это было неизменным (отсюда frozen=True), так что foo.id = bar и foo.name = baz терпят неудачу. Но я хочу иметь возможность удалить идентификатор, например, так:

foo = Foo(id=10, name="spam")

foo.strip_id()
foo
-> Foo(id=None, name="spam")

Я пробовал несколько вещей, переопределяя setattr , но ничего не получалось. Есть ли элегантное решение для этого? (Я знаю, что мог бы написать метод, который возвращает новый замороженный экземпляр, который идентичен, за исключением того, что этот идентификатор был удален, но это выглядит немного странно, и это потребовало бы от меня сделать foo = foo.strip_id(), так как foo.strip_id() на самом деле не будет изменить foo)

Редактировать:

Хотя некоторые комментаторы, похоже, не согласны, я думаю, что есть законное различие между «полностью изменяемым, делай, что хочешь с ним» и «неизменным, за исключением этого конкретного, строго контролируемого способа "

Ответы [ 2 ]

1 голос
/ 16 марта 2020

Как небольшое улучшение по сравнению с решением уровня Z4, пожалуйста, используйте object.__setattr__ вместо self.__dict__ для управления атрибутами замороженного класса данных. Тот факт, что классы используют словарь для хранения своих атрибутов, является просто поведением по умолчанию, и классы данных, в частности, будут регулярно использовать __slots__ вместо этого, потому что это уменьшает объем памяти.

from dataclasses import dataclass

@dataclass(frozen=True)
class Foo:
    id: str
    name: str

    def strip_id(self):
        object.__setattr__(self, 'a', None)   
1 голос
/ 16 марта 2020

Ну, вы можете сделать это, , непосредственно изменив __dict__ член экземпляра , изменив атрибут с помощью object.__setattr__(...) 1 , но почему ??? Спрашивать конкретно о неизменяемом , а затем сделать его изменчивым ... нерешительно. Но если вы должны:

from dataclasses import dataclass

@dataclass(frozen=True)
class Foo:
    id: str
    name: str
    def strip_id(self):
        object.__setattr__(self, 'id', None)

foo=Foo(10, 'bar')

>>> foo
Foo(id=10, name='bar')
>>> foo.strip_id()
>>> foo
Foo(id=None, name='bar')

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

Если вы Вы используете это как сигнал другим программистам, что им не следует изменять значения, способ, который обычно делается в Python, заключается в добавлении префикса имени переменной с одним подчеркиванием. Если вы хотите сделать это, в то же время делая доступными значения, возможно, будет работать что-то вроде этого:

from dataclasses import dataclass

@dataclass
class Foo:
    _id: str
    _name: str
    def strip_id(self):
        self.__dict__['id'] = None
    def get_id(self):
        return self._id

1: Изменено согласно ответу @Arne, который заметил, что внутреннее использование словаря в качестве контейнера данных не гарантируется.

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