首页 > 解决方案 > Python 使可变对象的行为不可变

问题描述

编辑:我没有改变对象的可变性,只是让变量表现得像它。我没有考虑具体的实现,但可能会使用它来使具有可变项的元组可散列

我知道在 python 中,您可以使不可变对象的行为类似于可变对象 - 特别是它们不可散列,并且您可以编辑对象而无需创建新对象 - 通过将其包含在列表中,因此它的行为类似于 C指针:

myVar = 5
myVar2 = myVar
#both point to same object 5
myVar += 1
#myVar2 still 5, myVar has new object 6

myVar = [5]
#myVar is no longer immutable
myVar2 = myVar
myVar[0] += 1
#myVar2 is now also 6

这意味着您可以更改变量指向的对象的值,它将影响引用该对象的所有变量

但是有没有一种相反的方法 - 使可变对象的行为不可变(并且是可散列的)?元组不会这样做 - 如果元组中的值是可变的,则可以更改它,并且具有可变对象的元组是不可散列的。我想这样做主要是为了它可以像不可变对象一样存储,其中具有相同值的变量都指向同一个对象,并且对变量的更改会导致新对象而不影响其他变量,或者我必须基本上用 OOP 重新实现 python 名称方法?

摘要:我想要一个可变对象的不可变包装器,与元组不同,它不允许编辑项目,并且是可散列的

标签: python-3.x

解决方案


Python 没有变量。它有名字。所有名称都指向一个对象。

归因使名称指向对象。就是这么简单。您不能使归因隐含地生成新对象。

a = x
b = a # means the name "b" also refers to x - regardless if x is mutable or not

所以不,你不能以这种方式使可变对象不可变 - 如果有人获得对可变对象的引用,他们可以改变它,并且对同一对象的所有引用都会受到影响。

仅仅因为一个对象是不可变的,并不会使具有相同值的对象自动相同 - 试试这个:

>>> x = 'foo bar'
>>> y = ' '.join(['foo', 'bar'])
>>> x == y
True
>>> x is y
False
>>> id(x), id(y)
(139789175899936, 139789175899984)

如您所见,它们是两个不同的不可变对象,具有相同的值!两者分别存在于内存中!

但是,要使它们可散列是可能的-您必须定义一个__hash__返回一些唯一散列的方法,当对象相等时比较相等-实际上,默认情况下,所有对象都已经将 id 作为散列返回,因此您可以包装在一个类中:

class HashableContainer:
    def __init__(self, obj):
        self._obj = obj

l = [] # mutable, no hash
ic = HashableContainer(l) # mutable, but hashable

d = {}
d[ic] = 'test' # can be used as key

但是有一个警告 - 如果您生成HashableContainer具有相同内容的新对象,它将被视为不同的对象 - 因为它将具有不同的哈希(基于其 id)


推荐阅读