深入python的set和dict
dict的abc继承关系
在 Python 中,dict
类是内置的字典类型,用于存储键值对。Python 的 collections.abc
模块提供了一组抽象基类(ABCs),这些基类定义了各种容器类型应该实现的方法。这些抽象基类可以用来检查一个类是否提供了特定的接口,即它是否实现了特定的方法。
dict
的 ABC 继承关系
dict
类主要与以下几个抽象基类相关:
-
collections.abc.Mapping
:- 这是所有映射类型的抽象基类。映射对象表示键到值的映射,并且它们保持元素的插入顺序。
dict
是这个类的一个具体实现,它提供了映射所需的所有方法,如__getitem__
,__setitem__
,__delitem__
,__iter__
, 和__len__
。
- 这是所有映射类型的抽象基类。映射对象表示键到值的映射,并且它们保持元素的插入顺序。
-
collections.abc.MutableMapping
:- 这个抽象基类扩展了
Mapping
,添加了可以修改映射的方法,如pop
,popitem
,clear
,update
, 和setdefault
。dict
同样实现了这个接口,使其不仅可以读取,还可以修改字典内容。
- 这个抽象基类扩展了
-
collections.abc.Collection
:- 这个抽象基类是
Iterable
,Sized
, 和Container
的综合体。它提供了一些基本的集合操作。dict
通过继承Mapping
间接继承了Collection
。
- 这个抽象基类是
-
collections.abc.Iterable
:- 这个接口要求实现
__iter__()
方法,返回一个迭代器。dict
通过其键、值或键值对可以被迭代。
- 这个接口要求实现
-
collections.abc.Sized
:- 这个接口要求实现
__len__()
方法,返回容器中元素的数量。dict
实现了这个方法,可以通过len()
函数获取字典的大小。
- 这个接口要求实现
-
collections.abc.Container
:- 这个接口要求实现
__contains__()
方法,用于检查容器是否包含某个元素。在dict
的情况下,这通常用来检查某个键是否存在。
- 这个接口要求实现
总结
dict
类是 Python 中的一个非常强大的内置数据结构,它不仅实现了 MutableMapping
的所有方法,还通过继承链实现了其他几个重要的抽象基类的方法。这些抽象基类为 dict
提供了规范的行为和接口,使得 dict
可以与其他实现了相同接口的自定义类在多态性方面兼容。这种设计也便于开发者在创建自己的类时,通过继承或实现这些抽象基类来确保它们的类具有一致的接口和行为。
dict的常用方法
Python 中的 dict
类提供了多种方法来操作和管理字典。这些方法可以帮助你有效地添加、删除、查找和操作字典中的键值对。以下是一些最常用的 dict
方法:
1. get(key[, default])
返回字典中键 key
对应的值。如果键不存在,则返回 default
,默认为 None
。
d = {'a': 1, 'b': 2}
print(d.get('a')) # 输出: 1
print(d.get('c', 3)) # 输出: 3
2. setdefault(key[, default])
如果字典中包含有给定键,则返回该键对应的值。否则,将键值对 key = default
插入到字典中,并返回 default
。
d = {'a': 1, 'b': 2}
print(d.setdefault('a', 3)) # 输出: 1
print(d.setdefault('c', 3)) # 输出: 3
print(d) # 输出: {'a': 1, 'b': 2, 'c': 3}
3. update([other])
更新字典,添加或覆盖来自另一个字典或可迭代对象(必须是键值对)的键值对。
d = {'a': 1, 'b': 2}
d.update({'b': 10, 'c': 3})
print(d) # 输出: {'a': 1, 'b': 10, 'c': 3}
4. keys()
返回一个字典视图对象,包含字典的键。
d = {'a': 1, 'b': 2}
print(d.keys()) # 输出: dict_keys(['a', 'b'])
5. values()
返回一个字典视图对象,包含字典的值。
d = {'a': 1, 'b': 2}
print(d.values()) # 输出: dict_values([1, 2])
6. items()
返回一个字典视图对象,包含字典的键值对。
d = {'a': 1, 'b': 2}
print(d.items()) # 输出: dict_items([('a', 1), ('b', 2)])
7. pop(key[, default])
删除字典给定键 key
所对应的值,返回这个值。如果键不存在,则返回 default
,如果没有指定 default
,则抛出 KeyError
。
d = {'a': 1, 'b': 2}
print(d.pop('b')) # 输出: 2
print(d) # 输出: {'a': 1}
8. popitem()
随机返回并删除字典中的一个键值对(在 Python 3.7+ 中是最后插入的键值对)。
d = {'a': 1, 'b': 2}
print(d.popitem()) # 输出: ('b', 2)
print(d) # 输出: {'a': 1}
9. clear()
清空字典中的所有项。
d = {'a': 1, 'b': 2}
d.clear()
print(d) # 输出: {}
10. copy()
返回字典的一个浅复制。
d = {'a': 1, 'b': 2}
d2 = d.copy()
print(d2) # 输出: {'a': 1, 'b': 2}
这些方法使得 dict
类在 Python 中非常灵活和强大,能够满足大多数与字典相关的需求。
dict的子类
在 Python 中,dict
类是一个非常强大的内置类型,它提供了基本的映射功能。除了标准的 dict
类,Python 的标凈库也提供了几个有用的 dict
子类,这些子类扩展了 dict
的功能以满足特定的用途。下面是一些常见的 dict
子类:
1. collections.OrderedDict
在 Python 3.7 之前,标准的 dict
类不保证键的顺序。OrderedDict
是一个特殊的字典子类,它保持了元素被添加的顺序。在 Python 3.7 及以后,标准的 dict
类已经被改进,现在默认保持插入顺序,但 OrderedDict
仍然有一些特殊功能,如它的 popitem
方法可以用来弹出第一个或最后一个元素。
2. collections.defaultdict
defaultdict
是一个字典子类,它提供了一个默认值,用于字典试图访问不存在的键时。你可以提供一个函数,这个函数在需要时会被调用来提供默认值。这可以简化代码并避免检查键是否存在。
3. collections.Counter
Counter
是一个专门用于计数的字典子类。它用于计算可哈希对象(如列表中的元素)的出现次数。它包含了多个有用的计数相关的方法和模式,如 most_common()
来获取出现次数最多的元素。
4. types.MappingProxyType
这不是一个真正的字典子类,而是一个返回字典的只读视图的函数。这可以用来创建一个不可修改的字典视图,任何尝试修改视图的操作都会抛出异常。
示例代码
下面是这些 dict
子类的简单示例:
from collections import OrderedDict, defaultdict, Counter
from types import MappingProxyType
# OrderedDict
ordered_dict = OrderedDict([('a', 1), ('b', 2), ('c', 3)])
print(ordered_dict) # 输出: OrderedDict([('a', 1), ('b', 2), ('c', 3)])
# defaultdict
def_dict = defaultdict(int) # 默认值为 int,即 0
def_dict['a'] += 1
print(def_dict) # 输出: defaultdict(<class 'int'>, {'a': 1})
# Counter
counter = Counter(['a', 'b', 'a', 'c', 'b', 'a'])
print(counter) # 输出: Counter({'a': 3, 'b': 2, 'c': 1})
print(counter.most_common(1)) # 输出: [('a', 3)]
# MappingProxyType
original_dict = {'a': 1, 'b': 2}
read_only_dict = MappingProxyType(original_dict)
print(read_only_dict) # 输出: {'a': 1, 'b': 2}
# read_only_dict['a'] = 3 # 尝试修改会抛出 TypeError
这些子类通过提供额外的功能和特性,使得 dict
类在 Python 中的应用更加灵活和强大。
set和frozenset
在 Python 中,set
和 frozenset
都是内置的数据结构,用于存储唯一的元素。这两种类型的主要区别在于 set
是可变的,而 frozenset
是不可变的。这意味着一旦创建了 frozenset
,就无法更改其内容(添加或删除元素),而 set
可以随时修改。
set
set
是一个无序的集合类型,支持数学上的标准集合操作,如并集、交集、差集和对称差集。set
可以使用大括号 {}
或者 set()
函数来创建。它主要用于去除重复元素、集合运算等场景。
常用的 set
操作包括:
add(elem)
:向集合中添加一个元素。remove(elem)
:从集合中移除一个元素,如果元素不存在则抛出KeyError
。discard(elem)
:从集合中移除一个元素,如果元素不存在也不会抛出错误。pop()
:随机移除一个元素并返回该元素,如果集合为空则抛出KeyError
。clear()
:清空集合中的所有元素。union()
、intersection()
、difference()
、symmetric_difference()
:执行并集、交集、差集和对称差集操作。
frozenset
frozenset
是不可变的,可以作为字典的键或者另一个集合的元素。它的行为和 set
类似,但不支持任何修改集合的操作,如添加或删除元素。frozenset
可以通过 frozenset()
函数创建。
frozenset
支持的操作主要是:
- 所有不修改集合的
set
操作,如union()
、intersection()
等。 - 由于其不可变性,
frozenset
可以用作字典的键。
示例代码
# 创建 set 和 frozenset
s = set([1, 2, 3, 2, 1])
fs = frozenset([1, 2, 3, 2, 1])
print("set:", s) # 输出: set: {1, 2, 3}
print("frozenset:", fs) # 输出: frozenset: frozenset({1, 2, 3})
# 修改 set
s.add(4)
print("Modified set:", s) # 输出: Modified set: {1, 2, 3, 4}
# 尝试修改 frozenset (会抛出 AttributeError)
# fs.add(4)
# 集合运算
print("Union:", s.union(fs)) # 输出: Union: {1, 2, 3, 4}
print("Intersection:", s.intersection(fs)) # 输出: Intersection: {1, 2, 3}
总的来说,set
和 frozenset
在 Python 中提供了强大的集合处理功能,选择使用哪一个取决于你是否需要修改集合的内容。
dict和set的实现原理
在 Python 中,dict
(字典)和 set
(集合)是基于哈希表的数据结构。这些数据结构的实现原理涉及到一系列有趣的技术,使它们在执行查找、插入和删除操作时非常高效。以下是这两种数据结构实现原理的概述:
1. 哈希表(Hash Table)
哈希表是一种使用哈希函数组织数据的数据结构,以便快速访问存储在表中的数据项。哈希函数将输入(例如字典的键或集合的元素)转换为存储位置的索引。
哈希冲突
当两个不同的输入值经过哈希函数处理后得到相同的输出索引时,就会发生哈希冲突。Python 的字典和集合通过以下方式处理哈希冲突:
- 开放寻址法:如果发生冲突,哈希表会尝试找到下一个空闲的槽位来存储新的元素。
- 链地址法:每个槽位开始是一个链表的头节点。如果多个元素哈希到同一个槽位,它们会被添加到这个槽位的链表中。
Python 主要使用开放寻址法来解决冲突。
2. 动态调整大小
为了保持操作的高效性,Python 的 dict
和 set
会根据元素的数量动态调整哈希表的大小。这意味着当哈希表的负载因子(已存储的元素数与槽位总数的比率)达到一定阈值时,哈希表的容量会增加,通常是加倍。然后,现有元素会被重新哈希到新的槽位中。
3. 字典(dict)
Python 的字典存储键值对。它使用键的哈希值来确定存储位置:
- 键必须是可哈希的:这意味着键必须是不可变类型,如整数、浮点数、字符串、元组等。
- 查找时间复杂度:在理想情况下,字典操作(插入、查找和删除)的时间复杂度为 O(1)。在最坏的情况下(例如,当所有键都哈希到同一个槽位时),这些操作的时间复杂度将退化到 O(n)。
4. 集合(set)
集合在内部实现上与字典非常相似,但它只存储键,没有值。集合用于存储唯一元素,并提供高效的成员检查、添加和删除操作。
- 元素也必须是可哈希的。
- 操作的时间复杂度:与字典类似,集合的基本操作通常是 O(1),在哈希冲突极端的情况下会退化到 O(n)。
总结
Python 中的 dict
和 set
是基于高效的哈希表实现的,它们提供了快速的数据存取操作。这些结构的动态大小调整和冲突解决策略进一步优化了它们的性能,使得在大多数情况下,它们的操作都非常迅速。