简体   繁体   English

Python:为什么我可以将可变对象放入dict或set中?

[英]Python: why can I put mutable object in a dict or set?

Given the following example, 给出以下示例,

class A(object):
    pass
a = A()
a.x = 1

Obviously a is mutable, and then I put a in a set, 显然,a是可变的,然后我将a放入集合中,

set([a])

It succeeded. 成功了。 Why I can put mutable object like "a" into a set/dict? 为什么我可以将可变对象(例如“ a”)放入集合/字典中? Shouldn't set/dict only allow immutable objects so they can identify the object and avoid duplication? 设置/命令是否不应该只允许不可变的对象,以便它们可以识别对象并避免重复?

Python doesn't test for mutable objects, it tests for hashable objects. Python不会测试可变对象,而是测试可哈希对象。

Custom class instances are by default hashable. 自定义类实例默认情况下是可哈希的。 That's fine because the default __eq__ implementation for such classes only tests for instance identity and the hash is based of the same information. 很好,因为此类的默认__eq__实现仅测试实例身份 ,并且哈希基于相同的信息。

In other words, it doesn't matter that you alter the state of your instance attributes, because the identity of an instance is immutable anyway. 换句话说,更改实例属性的状态并不重要,因为实例的身份始终是不可变的。

As soon as you implement a __hash__ and __eq__ method that take instance state into account you might be in trouble and should stop mutating that state. 一旦实现考虑实例状态的__hash____eq__方法,您可能会遇到麻烦,应该停止对该状态进行__hash__ Only then would a custom class instance no longer be suitable for storing in a dictionary or set. 只有这样,自定义类实例才不再适合存储在字典或集合中。

From the docs the requirement are that it must be hashable and can be compared: 文档开始 ,要求它必须是可哈希的并且可以比较:

An object is hashable if it has a hash value which never changes during its lifetime (it needs a hash () method), and can be compared to other objects (it needs an eq () or cmp () method). 如果对象的哈希值在其生命周期内始终不变(需要使用hash ()方法),并且可以与其他对象进行比较(需要使用eq ()或cmp ()方法),则该对象是可哈希的。 Hashable objects which compare equal must have the same hash value. 比较相等的可哈希对象必须具有相同的哈希值。

Hashability makes an object usable as a dictionary key and a set member, because these data structures use the hash value internally. 散列性使对象可用作字典键和set成员,因为这些数据结构在内部使用散列值。

All of Python's immutable built-in objects are hashable, while no mutable containers (such as lists or dictionaries) are. Python的所有不可变内置对象都是可哈希的,而没有可变容器(例如列表或字典)是可哈希的。 Objects which are instances of user-defined classes are hashable by default ; 作为用户定义类实例的对象默认情况下是可哈希的 they all compare unequal (except with themselves), and their hash value is their id(). 它们都比较不相等(除了它们本身),它们的哈希值是其id()。

You can see from the last part that user defined classes (emphasis is mine) are hashable by default 您可以从最后一部分看到,用户定义的类(强调是我的)默认情况下是可哈希的

There is no mention in the docs about mutability requirements for set : 文档中没有提及set可变性要求:

class set([iterable]) class frozenset([iterable]) Return a new set or frozenset object whose elements are taken from iterable. class set([iterable])class Frozenset([iterable])返回一个新的set或Frozenset对象,其元素来自于iterable。 The elements of a set must be hashable. 集合的元素必须是可哈希的。 To represent sets of sets, the inner sets must be frozenset objects. 为了表示集合集,内部集合必须是Frozenset对象。 If iterable is not specified, a new empty set is returned.) 如果未指定iterable,则返回一个新的空集。)

For a dict again the requirement is that the key is hashable: 同样,对于dict ,要求键是可哈希的:

A mapping object maps hashable values to arbitrary objects. 映射对象将可哈希值映射到任意对象。 Mappings are mutable objects. 映射是可变的对象。 There is currently only one standard mapping type, the dictionary. 当前只有一种标准映射类型,即字典。 (For other containers see the built in list, set, and tuple classes, and the collections module.) (有关其他容器,请参见内置列表,集合和元组类,以及集合模块。)

After doing some more research, I was able to find out the reason why I would think set and dict only allow immutable objects as the entries and keys respectively, which is incorrect. 经过更多研究后,我能够找出为什么我认为set和dict只允许将不可变对象用作条目和键的原因,这是不正确的。 I think it's important for me to clarify that here as I'm sure there are people who are new to Python having the same doubt as I had before. 我认为对我来说很重要,因为我敢肯定,对于Python来说,有些新人与我以前的疑问一样。 There are two reasons for me to previously confuse immutability with hashability. 我以前有两个理由将不变性与哈希性混淆。 First reason is that all the built-in immutable objects (tuple, frozenset...) are allowed as set entries or dict keys, while all of the built-in mutable container objects are not. 第一个原因是允许将所有内置的不可变对象(元组,frozenset ...)用作集合条目或dict键,而不允许所有内置的可变容器对象。 Second reason is actually where this question was derived from. 第二个原因实际上是这个问题的来源。 I was reading the book Mastering Object-Oriented Python . 我正在读《 精通面​​向对象的Python 》一书。 In the part it explains the __hash__ function, it leads the reader into thinking immutability is the prerequisite of hashability. 在该部分说明了__hash__函数,它使读者认为不变性是哈希性的前提。

The definition of immutability is for an object, after its creation, you can't change the value of its existing attributes or creating new ones. 不变性的定义是针对对象,在创建对象后,您将无法更改其现有属性的值或创建新属性。 So it has nothing to do with hashability, which requires an object to have their __hash__() and __eq__() methods defined, and their hash value to be immutable during their lifecycle. 因此,它与哈希性无关,后者要求对象定义其__hash__()__eq__()方法,并且其哈希值在其生命周期内是不可变的。 Actually hashable objects are immutable in terms of their hash value. 实际上,可哈希对象就其哈希值而言是不变的。 I guess that's one of the reason these two concepts get confused so often. 我想这就是这两个概念如此频繁混淆的原因之一。

the id does not change when you add members. 添加成员时, id不会更改。 so there is no reason for that not to work. 因此没有理由不起作用。

class A(object):
    pass
a = A()
print(id(a))

a.x = 1
print(id(a))

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM