揭秘 Python 字典的有趣特性
1
Python中的字典是一种灵活的数据结构,提供了高效的键值映射。尽管它们通常用于简单的查找和存储数据,但还有一些有趣且不太常见的方面和用例值得探索:
1、将列表作为字典的键
>>>_dict = {}
>>>_list = [1, 2, 3]
>>>_dict[_list] = 'Added'
#输出:
# 'list'类型不可被hash
TypeError: unhashable type: 'list'
事实上,如果我们将列表作为字典的键,上述代码会引发错误。原因是当我们将一个对象作为字典的键时,Python会调用该对象类的 __hash__
函数。
与int
、str
、tuple
等不同,列表类缺少 __hash__
方法的实现。
如果我们尝试扩展 list 类,在其中添加__hash__
方法,那么它将允许我们用列表作为字典的键。
现在如果我们尝试扩展列表类并在其中添加这个方法,它将允许我们将列表作为字典的键。
class ClassList(list):
def __hash__(self):
return 0
_dict = {}
_list = ClassList([1, 2, 3])
_dict[_list] = 'Added'
print(_dict)
输出:
{[1, 2, 3]: 'Added'}
但并不推荐使用上述方法,因为这将使列表可哈希化,从而导致其他不可预知的程序行为。
2、使用字典作为IF条件的替代方案
我们都知道字典的用途是维护键值对。但是,字典还可以作为IF条件的特殊用例使用。例如,考虑以下代码。根据输入值,调用相应的函数。
def funca():
print('a')
def funcb():
print('b')
num = 1
if num==1:
funca()
elif num==2:
funcb()
else:
print('error')
# 结果
a
使用字典:
def funca():
print('a')
def funcb():
print('b')
num = 1
funcMaping = {1: funca,2:funcb}
funcMaping.get(num,lambda:print('error'))()
# lambda:print('error')为默认函数
# 结果
a
可以看到,使用字典可以很容易的找到并执行对应的函数,同时这也提高了代码的可维护性。冗长的if-else通常是难以维护的代名词。
3、字典作为 switch 语句
您可以使用字典模拟switch语句,以获得更清晰和更易读的代码。例如:
def switch_case(case):
return {
'case1': 'This is case 1',
'case2': 'This is case 2',
'default': 'This is the default case'
}.get(case, 'Invalid case')
result = switch_case('case1')
以上代码将打印:“This is case 1”。
4、__missing__
方法
__missing__
方法从Python 2.5 开始,字典就加入了一个特殊的 __missing__
方法,用于处理缺失的项:
class MyDict(dict):
def __missing__(self, key):
self[key] = rv = []
return rv
m = MyDict()
m["foo"].append(1)
m["foo"].append(2)
print(dict(m)) # {'foo': [1, 2]}
print(m["x"]) # []
其实 python 还提供了字典的一个子类Defaultdict
,功能与上述方法类似,只是对于不存在的项,它调用了一个无参数的函数:
from collections import defaultdict
m = defaultdict(list)
m["foo"].append(1)
m["foo"].append(2)
print(dict(m)) # {'foo': [1, 2]}
当您想提供默认值或在找不到键时执行特定操作而不是引发KeyError
时,这非常有用。
5、哈希等价键
下面是一个有趣的字典示例:
my_dict = {'1': 'string', True: 'bool', 1: 'int', 1.0: float}
print(my_dict)
输出:
{'1': 'string', True: <class 'float'>}
尽管向 Python 字典添加了 4 个不同的键,但你能告诉为什么它只保留了其中的两个吗?
原因是在Python中,字典根据哈希等价性(使用hash()计算)而不是身份(使用id()计算)来查找键。
在这种情况下,毫无疑问,1.0、1和True具有不同的数据类型,并且也是不同的对象。
print(id(1), id(True), id(1.0))
print(type(1), type(True), type(1.0))
输出:
140407572928816 4308871808 140407573652336
<class 'int'> <class 'bool'> <class 'float'>
但事实上,它们共享相同的哈希值,因此字典将它们视为相同的键。
print(hash(1), hash(True), hash(1.0))
输出:
1 1 1
而且你是否注意到与 True 对应的值明明是 bool,但它打印的却是 float ?
输出:{'1': 'string', True: <class 'float'>}
这是因为,首先,将 True 作为键,其值为'bool'。接下来,当添加键 1 时,由于哈希值相同 Python 将其识别为同一个键。
因此,与 True 对应的值被 'int' 覆盖,而键(True)保持不变。
最后,当添加 1.0 时,又遇到了相同的情况,'int' 又被 'float' 覆盖。
总结
Python 中的字典功能强大且灵活,探索其各种特性和用例可以实现更高效和可读性更好的代码。
这篇文章探索了 Python 字典的一些有趣特性和用例。我们了解到,尽管列表不能直接作为字典的键,但通过扩展列表类并实现 __hash__
方法,我们可以实现这一点。此外,我们还发现字典可以作为 IF 条件的替代方案,通过提供键来调用相应的函数。另外,我们介绍了将字典用于实现 switch 语句的方法,以及字典的 __missing__
方法和 DefaultDict 的用法。最后,我们探讨了哈希等价键的现象。这些有趣的特性使得 Python 字典在编程中非常灵活和强大。无论是处理复杂的数据结构还是简化代码逻辑,我们都可以利用这些特性来提高代码的效率和可读性。