有没有推荐的方法来确保不变性

时间:2019-06-16 07:26:17

标签: python class immutability

我正在观察以下行为,因为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 个答案:

答案 0 :(得分:1)

  

我正在观察以下行为,因为python通过引用传递了对象?

不是。这是一个微妙的问题。您可以查看python - How do I pass a variable by reference? - Stack Overflow

我个人并不完全同意接受的答案,因此建议您使用Google call by sharing。然后,您可以对这个微妙的问题做出自己的决定。

  

我找到了copy.deepcopy()到deepcopy对象,以防止修改传递的对象。还有其他建议吗?

据我所知,如果不使用第三个软件包,没有其他更好的方法了。

答案 1 :(得分:0)

您可以使用__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

答案 2 :(得分:0)

并没有真正100%防水的方法,但是您可能会难以无意间突变想要保持冻结的对象;对于大多数人而言,推荐的方法可能是使用冻结的DataClass或冻结的attrs

@RaymonHettinger在他的talk on DataClasses(2018年)中提到了三种方法:一种方法是使用元类,另一种方法是在fractions module中使用属性只读属性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

->扩展__setattr____delattr__,并覆盖`哈希

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库还提供了创建不可变对象的选项。

相关问题