Есть ли рекомендуемый способ обеспечения неизменности - PullRequest
0 голосов
/ 16 июня 2019

Я наблюдаю следующее поведение, поскольку python передает объект по ссылке?

class Person(object):
    pass

person = Person()

person.name = 'UI'
def test(person):
    person.name = 'Test'

test(person)
print(person.name)

>>> Test

Я обнаружил copy.deepcopy () для объекта deepcopy, чтобы предотвратить изменение переданного объекта.Есть ли другие рекомендации?

import copy

class Person(object):
    pass

person = Person()

person.name = 'UI'
def test(person):
    person_copy = copy.deepcopy(person)
    person_copy.name = 'Test'

test(person)
print(person.name)

>>> UI


Ответы [ 3 ]

1 голос
/ 16 июня 2019

Я наблюдаю следующее поведение, поскольку python передает объект по ссылке?

Не совсем. это тонкий вопрос. Вы можете посмотреть на Python - Как передать переменную по ссылке? - Переполнение стека

Лично я не полностью согласен с принятым ответом и рекомендую вам google call by sharing. Затем вы можете принять собственное решение по этому тонкому вопросу.

Я обнаружил copy.deepcopy () для объекта deepcopy, чтобы предотвратить изменение переданного объекта. Есть ли другие рекомендации?

Насколько я знаю, другого лучшего способа нет, если вы не используете третий пакет.

0 голосов
/ 16 июня 2019

На самом деле нет 100% водонепроницаемого способа, но вы можете затруднить непреднамеренное изменение объекта, который хотите сохранить замороженным;рекомендуемый способ для большинства людей, вероятно, использовать замороженный DataClass или замороженный attrs класс

В своем докладе о DataClasses (2018),@RaymonHettinger упоминает три подхода: один способ - с метаклассом , другой - как в модуле фракций - для атрибутов свойство только для чтения ;DataClass модуль расширяет __setattr__ и __delattr__ и переопределяет __hash__:

-> использовать метакласс.

Хорошие ресурсы включают книги @DavidBeasleyи говорит на python.

-> дает атрибутам свойство только для чтения

class SimpleFrozenObject:
    def __init__(self, x=0):
        self._x = x
    @property
    def x(self):
        return self._x

f = SimpleFrozenObject()
f.x = 2  # raises AttributeError: can't set attribute

-> extended __setattr__ и __delattr__ и переопределяет ` hash

class FrozenObject:

    ...

    def __setattr__(self, name, value):
        if type(self) is cls or name in (tuple of attributes to freeze,):
            raise FrozenInstanceError(f'cannot assign to field {name}')
        super(cls, self).__setattr__(name, value)

    def __delattr__(self, name):
        if type(self) is cls or name in (tuple of attributes to freeze,):
            raise FrozenInstanceError(f'cannot delete field {name}')
        super(cls, self).__delattr__(name, value)

    def __hash__(self):
        return hash((tuple of attributes to freeze,))

    ...

Библиотека attrs также предлагает варианты создания неизменяемых объектов.

0 голосов
/ 16 июня 2019

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

Это не пуленепробиваемый;вы все еще можете получить доступ к __dict__, чтобы изменить объект, и вы также можете разморозить объект, отключив _frozen, и если само значение атрибута изменчиво, это не очень помогает (x.things.append('x') будет работать для спискаthings).

class Freezable:
    def freeze(self):
        self._frozen = True

    def __setattr__(self, key, value):
        if getattr(self, "_frozen", False):
            raise RuntimeError("%r is frozen" % self)
        super().__setattr__(key, value)


class Person(Freezable):
    def __init__(self, name):
        self.name = name


p = Person("x")
print(p.name)
p.name = "y"
print(p.name)
p.freeze()
p.name = "q"

выходы

x
y
Traceback (most recent call last):
  File "freezable.py", line 21, in <module>
    p.name = 'q'
RuntimeError: <__main__.Person object at 0x10f82f3c8> is frozen
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...