【问题标题】:in python Class object is immutable object ,but it can be modify,why?在python中类对象是不可变对象,但是可以修改,为什么?
【发布时间】:2017-10-26 04:59:10
【问题描述】:

我不熟悉 python。我发现 class objectinstance object 最近可以是字典的键。所以我假设类对象和实例是可变对象。正如我们众所周知,字典的键必须是不可变的对象,并且像元组一样必须包含不可变的对象。换句话说,如果使用元组作为字典的键。它不能包含列表对象等。但是类对象可以,类对象可以修改它的属性。它让我困惑了很长时间。请减轻我的负担。我不了解类命名空间的概念,实例命名空间以及它们之间的关系。你能给我解释一下吗?提前致谢。以下是我的测试

class Student(object):
   name='tests'
   pass

dic={Student:'test'} #not error
print(id(Student))
Student.name = 'modified'
print(id(Student))

【问题讨论】:

  • 结果是 38964680 38964680
  • 考虑一个元组t = (‘abc’, [1, 2, 3]) 上面的元组t包含不同数据类型的元素,第一个是不可变字符串,第二个是可变列表。元组本身是不可变的。即它没有任何改变其内容的方法。同样,字符串是不可变的,因为字符串没有任何变异方法。但是列表对象确实有变异方法,所以它可以被改变。这是一个微妙的点,但仍然很重要:不可变对象的“值”不能改变,但它的组成对象可以。参考 goo.gl/pRHJm7

标签: python class object


【解决方案1】:

您需要在这里小心。您正在混合 2 个独立(但密切相关)的概念。

第一个概念是不变性。不可变对象一旦创建就无法更改。

第二个概念是hashability——从一个在其生命周期内不会改变的对象构造一个一致的整数值并定义一个一致的相等函数的能力1 .请注意,这些约束非常很重要(我们将看到)。

后一个概念决定了什么可以用作字典键(或集合项)。默认情况下,类实例具有明确定义的相等函数(两个对象相等,如果它们具有相同的id())。类实例也有一个在其生命周期内不会改变的整数值(它们的id())。因为id() 也作为hash() 返回值公开,所以类的实例(以及作为type 实例的类本身)默认是可散列的。

class Foo(object):
    def __init__(self, a):
        self.a = a

f1 = Foo(1)
f2 = Foo(1)

d = {
    f1: 1,
    f2: 2,
}

我们的字典中有 2 个独立的 Foo 实例。即使它们相同,它们也不相等,并且具有不同的哈希值。

f1 == f2  # False -- They do not have the same id()
hash(f1) == hash(f2)  # False.  By default, the hash() is the id()

好的,但并非所有事物都是可散列的——例如listset 实例不可散列。在某些时候,引用相等不再那么有用了。例如我写:

d = {[1, 2, 3]: 6}
print(d[1, 2, 3])

我收到了KeyError。为什么?因为我的两个列表不是同一个列表——它们只是碰巧有相同的值。换句话说,它们相等,但它们没有引用相等。现在这才开始变得非常混乱。为了避免所有这些混淆,python 开发人员刚刚决定将列表的id() 暴露给列表的hash()。相反,他们提出了一个TypeError 并带有(希望)更有帮助的错误消息。

hash([])  # TypeError: unhashable type: 'list'

请注意,平等覆盖以做自然的事情,而不是通过id()进行比较:

l1 = [1]
l2 = [1]
l1 == l2  # True.  Nice.

好的,到目前为止,我们基本上已经说过,要将某些内容放入字典中,我们需要具有良好行为的 __hash____eq__ 方法,并且对象默认具有这些方法。一些对象选择删除它们以避免混淆情况。 不变性在哪里出现?

到目前为止,我们的世界包括能够将事物存储在表格中并通过对象的id()单独进行查找。这有时非常有用,但它仍然真的有限制。如果我只能依赖它们的id(),我将无法自然地在查找表中使用整数(如果我使用文字存储它,然后使用计算结果进行查找呢?)。幸运的是,我们生活在一个可以让我们解决这个问题的世界——不变性有助于构造一个hash()绑定到对象的@ 987654344@ 并且在对象的生命周期内没有改变的危险。这可能非常有用,因为现在我可以做到:

d = {(1, 2, 3): 4}
d[(1, 2) + (3,)] # 4!

现在我使用的两个元组不是同一个元组(它们没有相同的id()),但它们相等,因为它们是不可变的,我们可以构造一个hash() 函数使用tuple 的内容而不是id()。这个超级好用!请注意,如果元组是可变的并且我们试图玩这个把戏,我们将(可能)违反hash() 不应在对象的生命周期内改变的条件。


1这里的一致意味着如果两个对象相等,那么它们也必须具有相同的哈希值。这是解决哈希冲突所必需的,我不会在这里详细讨论...

【讨论】:

  • 感谢您的回答,我对不可变和哈希这两个概念感到困惑。谢谢你解决了我的疑惑。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-01-26
  • 2013-07-15
  • 1970-01-01
  • 2012-09-18
  • 1970-01-01
  • 2019-04-27
  • 1970-01-01
相关资源
最近更新 更多