在python类对象是不可变对象,但它可以修改,为什么?

时间:2017-10-26 04:59:10

标签: python class object

我不熟悉python。我发现类对象实例对象最近可以成为字典的关键。所以我假设类对象和实例是可变对象。当我们所有kown,字典的键必须是不可变对象,并且像元组必须包含不可变对象。换句话说,如果使用元组作为字典的键。它不能包含列表对象,等等。但是类对象可以,类对象可以修改它的属性。这让我困惑了很长时间。请让我感到轻松。我不理解类命名空间概念,实例命名空间和彼此之间的关系。你能帮我解释一下吗?提前致谢。以下是我的测试

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

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

1 个答案:

答案 0 :(得分:0)

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

第一个概念是不变性。一旦你创建了不可变对象,它们就无法改变。

第二个概念是 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,
}

这里我们在字典中有两个单独的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__方法,并且默认情况下这些对象都有。某些对象选择删除它们以避免混淆情况。 immutability 在哪里进入?

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

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

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

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