【python】dict(7)

本文全面介绍了Python字典的创建、访问、操作及复制方法,并详细解析了字典的内部存储机制,同时提供了实用的字典操作技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

参考

Note: 更多连载请查看【python】


最近一次修订时间为 2020-11-04



这里写图片描述
在这里插入图片描述
图片来源:https://www.bilibili.com/video/av20982396/?p=8

字典是一些列键(key)值(value)对组成的,形式如下:{键1:值1,键2:值2}
每个键值对用冒号隔开,每对之间用逗号隔开

1 字典的创建

1.1 用大括号{}创建

dict1 = {'Bob':18,'Alice':17}#创建字典
print (dict1)
type(dict1)

ouput

{'Bob': 18, 'Alice': 17}
dict

键必须是唯一的,必须是不可变的,如字符串,数字,元组,值可以是任何数据类型

dict2 = {'慧慧':[1,'a',[2.33,'Python']],1:('Hello World!')}
dict2

output

{1: 'Hello World!', '慧慧': [1, 'a', [2.33, 'Python']]}

可以看出,字典的value可以是元组,也可以是列表


1.2 创建空字典

dict3 = {}#创建空字典
print (dict3)
type (dict3)

output

{}
dict

1.3 用 dict() 创建(不推荐)

除了直接用大括号创建字典,还可以用dict()来创建字典(不推荐)
用法如下:

  • 通过放入列表套元组或者元组套列表实现

  • 或者列表套列表元组套元组,还记得在元组里讲到的列表和元组的相互转换么?

1.3.1 列表套元组

dict4 = dict([('Bob',18),('Alice',17)]) # 除了直接用大括号创建字典,还可以用dict()来创建字典
dict4

output

{'Alice': 17, 'Bob': 18}

1.3.2 元组套列表

dict5 = dict((['Bob',18],['Alice',17]))
dict5

结果同上

1.3.3 元组套元组

dict6 = dict((('Bob',18),('Alice',17)))

结果同上

1.3.4 列表套列表

dict7 = dict([['Bob',18],['Alice',17]])

结果同上

补充:下边这些代码,他们都在执行一样的操作吗?你看得出差别吗?

a = dict(one=1, two=2, three=3)
b = {'one': 1, 'two': 2, 'three': 3}
c = dict(zip(['one', 'two', 'three'], [1, 2, 3]))
d = dict([('two', 2), ('one', 1), ('three', 3)])
e = dict({'three': 3, 'one': 1, 'two': 2})

测试下

a == b == c == d == e

output

True

1.4 借助 dict() 和等号来创建

这种创建字典情况下,键只能为字符串类型,并且创建的时候字符串不用加引号

dict8 = dict(Bob = 18 , Alice = 17)#这种创建字典情况下,键只能为字符串类型,并且创建的时候字符串不用加引号
dict8

结果同上
Note1:如果加了引号,会报错
这里写图片描述

SyntaxError: keyword can’t be an expression

Note2:用这种方法不能创建键为数值型的字典
这里写图片描述

SyntaxError: keyword can’t be an expression

用如下方式则可以

dict9 = {1:18 , 2:17}
dict9

output

{1: 18, 2: 17}

2 访问字典中的值

这里写图片描述
上面是根据 keys 来访问 values,下面试试根据 values 来访问 keys

dict1 = {"a":1,"b":2,"c":3}
print(dict1.keys())
print(dict1.values())

output

dict_keys(['a', 'b', 'c'])
dict_values([1, 2, 3])

实现下访问功能

def reverse_lookup(dict1,value1):
    for i in dict1.keys():
        if dict1[i] == value1:
            return i
    print("no value")

测试下效果

reverse_lookup(dict1,2)

output

'b'

再试试,没有的 values

reverse_lookup(dict1,4)

output

no value

3 添加字典的键值对

dict1 = {'Alice': 17, 'Bob': 18}
dict1['Tom'] = 19 #添加键值对
print (dict1)

output

{'Alice': 17, 'Bob': 18, 'Tom': 19}

4 修改字典的键值对

dict1 = {'Alice': 17, 'Bob': 18}
dict1['Alice'] = 18 #更新修改键值对
print (dict1)

output

{'Alice': 18, 'Bob': 18}

5 删除字典的键值对

dict1 = {'Alice': 17, 'Bob': 18}
del dict1['Bob'] #删除键值对
print (dict1)

output

{'Alice': 17}

6 字典的遍历

dict1 = {'Alice': 18, 'Tom': 19}
for key in dict1.keys():#遍历字典只需要遍历它的键
    print(key,dict1[key])

output

Alice 18
Tom 19

或者直接

dict1 = {'Alice': 18, 'Tom': 19}
for i in dict1:
    print(dict1[i])

output

18
19

7 字典的复制

复制分为潜复制和深复制,浅复制是复制品A1会随着被复制品A的改变而改变,深复制是复制品A1不会随着A的改变而变动,复制以后独立了。我们常常理解的复制对应着深复制。

7.1 浅复制

下面看一下浅复制

dict1 = {'Bob': None, 'Tom': 19}
dict2 = dict1
print (dict2)
dict1['Bob'] = 19
print(dict1)
print(dict2)

output

{'Bob': None, 'Tom': 19}
{'Bob': 19, 'Tom': 19}
{'Bob': 19, 'Tom': 19}

可以看出,dict2随着dict1的改变而改变
我们来看看他们的内存地址

print (id(dict1))
print (id(dict2))

output

140113040589088
140113040589088

结果表明,dict1和dict2虽然是两个变量,但是指向着同一块内存空间


7.2 深复制

深拷贝可以用引入 copy 模块实现

import copy#深拷贝可以用引入copy模块实现
dict1 = {'Bob': None, 'Tom': 19}
dict2 = copy.deepcopy(dict1)
print(dict2)
dict1['Bob'] = 19
print(dict2)
dict1.clear()
print(dict1)
print(dict2)

output

{'Bob': None, 'Tom': 19}
{'Bob': None, 'Tom': 19}
{}
{'Bob': None, 'Tom': 19}

结果表明,dict2不会随着dict1的改变而改变,是深度复制,看看他们的内存地址

print(id(dict1))
print(id(dict2))

output

140113040575512
140113040589952

dict1 和 dict2 是内存地址不同的两个变量


8 字典的一些基本方法

8.1 keys()

返回包含字典所有key的列表

dict1 = {'Alice': 17, 'Bob': 18}
dict1.keys()#返回包含字典所有key的列表

output

dict_keys(['Alice', 'Bob'])

8.2 values()

返回包含字典所有value的列表

dict1 = {'Alice': 17, 'Bob': 18}
dict1.values()#返回包含字典所有value的列表

output

dict_values([17, 18])

8.3 items()

返回包含所有(键,值)项的列表

dict1 = {'Alice': 17, 'Bob': 18}
dict1.items()#返回包含所有(键,值)项的列表

output

dict_items([('Alice', 17), ('Bob', 18)])

8.4 clear()

删除字典中的所有项或元素,无返回值 (注意,不是删除字典,而是清空字典内容)

dict2 = {'乐子': [1, 'a', [2.33, 'Python']], 1: 'Hello World!'}
dict2.clear()#删除字典中的所有项或元素,无返回值(注意,不是删除字典,而是清空字典内容)
dict2

output

{}

8.5 get()

返回字典中key对应的值,若key不存在,则返回 default 的值(default 不设定默认为 None),区别于第二节访问字典中的值dict[key] 的方法,dict[key] 当key不存在时会报错,而get()是返回 default 的值

dict1 = {'Alice': 18, 'Tom': 19}
print (dict1.get('Alice'))#返回字典中key对应的值,若key不存在,则返回default的值(default默认为None)
print (dict1['Alice'])
print (dict1.get('Bob'))

output

18
18
None

得益于此,你的代码不会中断

dict1 = {'Alice': 18, 'Tom':19}
print(dict1.get('Bob', 20)) # 如果键不在字典中返回默认值 None 或者设定的默认值
print(dict1)

output

20
{'Alice': 18, 'Tom': 19}

也可以利用 get 来赋值

dit = {}
dit["1"] = dit.get("1", 1)
print(dit)

output

{'1': 1}

8.6 pop()

如果字典中存在key,则删除并返回 key 对应的 value;如果key不存在,且没有给出default值,则引发KeyError异常

dict1 = {'Alice': 18, 'Tom': 19}
a = dict1.pop('Alice')#如果字典中存在key,则删除并返回key对应的value;如果key不存在,且没有给出default值,则引发KeyError异常
print (a)
print (dict1)

output

18
{'Tom': 19}

Python 字典 popitem() 方法随机返回并删除字典中的一对键和值。
如果字典已经为空,却调用了此方法,就报出KeyError异常。
Python 字典 popitem() 方法

dict1 = {'Alice': 18, 'Tom': 19}
a = dict1.popitem()
print (a)
print(dict1)

output

('Tom', 19)
{'Alice': 18}

8.7 setdefault()

如果字典不存在key,则由dict[key] = default为其赋值

dict1 = {'Alice': 18, 'Tom': 19}
dict1.setdefault('Bob')#如果字典不存在key,则由dict[key] = default为其赋值
dict1

output

{'Alice': 18, 'Bob': None, 'Tom': 19}

8.8 update()

dict1.update(dict2) 是将字典dict2中键值对添加到dict1中

dict1 = {'Alice': 18, 'Tom': 19}
dict2 = {'a':1,'b':2}
dict1.update(dict2)#将字典dict2中键值对添加到dict1中
dict1

output

{'Alice': 18, 'Tom': 19, 'a': 1, 'b': 2}

9 dict 的存储方式

[扩展阅读] 你知道 Python 的字典(Dict)是如何存储的吗?

Python 的字典有好多个名称(“映射”、“哈希”、“散列"或者"关系数组”),那你知道为什么字典会被称为 Hash(翻译为"哈希"或"散列")吗?

你知道为什么字典对于键(Key)的存储数据要求比较严格,但对于对应的值(Value)的存储却要求很宽松吗?

读完这篇文章,你将深刻理解这些问题背后的真相!

首先我们来解释一下什么是 Hash,来自维基百科:

散列函数(或散列算法,又称哈希函数,英语:Hash Function)是一种从任何一种数据中创建小的数字“指纹”的方法。散列函数把消息或数据压缩成摘要,使得数据量变小,将数据的格式固定下来。该函数将数据打乱混合,重新创建一个叫做散列值的指纹。散列值通常用来代表一个短的随机字母和数字组成的字符串。好的散列函数在输入域中很少出现散列冲突。在散列表和数据处理中,不抑制冲突来区别数据,会使得数据库记录更难找到。

世界上没有两片完全相同的树叶,也没有两个相同的指纹,散列函数是用于从数据中创建小的数字指纹的方法。

我们看下图示:

如图,Python 调用内部的散列函数,将键(Key)作为参数进行转换,得到一个唯一的地址(这也就解释了为什么给相同的键赋值会直接覆盖的原因,因为相同的键转换后的地址是一样滴),然后将值(Value)存放到该地址中。

对于 Python 来说,键(Key)必须是可哈希的换句话说就是要可以通过散列函数计算出唯一地址的。那如果拿一个变量当键(Key)可以吗?肯定不行。因为变量随时都可能改变,不符合可哈希原则!

同样的,列表、字典、集合这些都是可变的,所以都不能做为键(Key)来使用。

那有朋友可能会问,那元组呢?元组总该是不变的吧?

其实不然,因为元组里边可以存放列表这类可变因素,所以如果实在想拿元组当字典的键(Key),那必须对元组做限制:元组中只包括像数字和字符串这样的不可变元素时,才可以作为字典中有效的键(Key)。

另外还需要注意的一点是,Python 的哈希算法对相同的值计算得到的结果是一样的,也就是说 12315 和 12315.0 的值相同,他们被认为是相同的键(Key)。

在这里插入图片描述

来自 https://stackoverflow.com/questions/32209155/why-can-a-floating-point-dictionary-key-overwrite-an-integer-key-with-the-same-v/32211042#32211042

1 == 1.0
hash(1) == hash(1.0)

output

True
True

Q1: 成员资格操作符(in 和 not in)可以检查一个元素是否存在序列中,当然也可以用来检查一个键(Key)是否存在字典中,那么请问哪种的检查效率更高些?为什么?

答:在字典中检查键(Key)是否存在比在序列中检查指定元素是否存在更高效。因为字典的原理是使用哈希算法存储,一步到位,不需要使用查找算法进行匹配,因此时间复杂度是O(1),效率非常高。(关于如何使用哈希算法存储的具体原理可以参考第3题的“扩展阅读”部分)

Q2:Python对键(Key)和值(Value)有没有类型限制?

答:Python对键的要求相对要严格一些,要求它们必须是可哈希(Hash)的对象,不能是可变类型(包括变量、列表、字典本身等)。但是Python对值是没有任何限制的,它们可以是任意的Python对象。

附录

合并两个字典({**a,**b})

dict1 = {1:"1",2:"2"}
dict2 = {3:"3",4:"4"}
dict1.update(dict2)
print(dict1)

output

{1: '1', 2: '2', 3: '3', 4: '4'}

或者用更简洁的方式

dict1 = {1:"1",2:"2"}
dict2 = {3:"3",4:"4"}
print({**dict1,**dict2})

output

{1: '1', 2: '2', 3: '3', 4: '4'}

简单的一个 | 也可以用于字典合并

dict1 = {1:"1",2:"2", 3:"3",}
dict2 = {3:"3",4:"4"}
dict3 = dict1 | dict2
print(dict3)

output

{1: '1', 2: '2', 3: '3', 4: '4'}

**dict

来自 Python 奇淫技巧!

双星“**”放在字典的前面可以让你将字典的内容作为命名参数传递给函数。字典的键是参数的名字,键的值作为参数的值传递给函数

dict1 = {"a":1, "b":2}
def f1(a, b):
    print(a+b)
    return 

f1(**dict1)
f1(a=1, b=2)

output

3
3

将两个列表转化为字典(zip)

先介绍下 zip 函数

zip() 函数名取自拉链zipper~

在这里插入图片描述
zip()函数接收两个或者多个序列,返回一个元组列表,每个元组包含来自每个序列中的一个元素

参考


先看一个简单的例子

list1 = [1, 2, 3, 4, 5]
tuple1 = (6, 7, 8, 9, 10)
s1 = "dream"

for item in zip(list1,tuple1,s1):
    print(item)

output

(1, 6, 'd')
(2, 7, 'r')
(3, 8, 'e')
(4, 9, 'a')
(5, 10, 'm')

再看看如何把两个列表变成字典

list1 = [1,2,3,4]
list2 = ["1","2","3","4"]
print(dict(zip(list1,list2)))

output

{1: '1', 2: '2', 3: '3', 4: '4'}

这里顺便介绍下 *,相当于解压操作

在这里插入图片描述


print(divmod(9, 4)) # 返回商和余数

output

(2, 1)

tuple1 = (9,4)
print(divmod(tuple1))

这样会报错

TypeError: divmod expected 2 arguments, got 1

用 * 解压一下就行

print(divmod(*tuple1))

output

(2, 1)

再看一个小例子

list1 = ["123","456"]
for i in zip(*list1):
    print(i)
('1', '4')
('2', '5')
('3', '6')

不使用 if-else 的 operator

import operator
action = {
    "+":operator.add,
    "-":operator.sub,
    "/":operator.truediv,
    "*":operator.mul,
    "**":pow
}

print(action["*"](2,3))

output

6

字典的排序

Python进阶系列连载(2)——那些容易被忽略的问题(中)

先看看对按 key 来排序

dict1 = {"f": 6, "a": 12, "b": 34, "d": 56}

for key in sorted(dict1):
    print(key,dict1[key])

output

a 12
b 34
d 56
f 6

更灵活的写法(来自 Python字典:竟还有我不会的高阶玩法?

dic = {'a': 2, 'b': 1, 'c': 3, 'd': 0}
lst1 = sorted(dic.items(), key=lambda x: x[0], reverse=False)
# [('a', 2), ('b', 1), ('c', 3), ('d', 0)]
lst2 = sorted(dic.items(), key=lambda x: x[1], reverse=False)
# [('d', 0), ('b', 1), ('a', 2), ('c', 3)]
print('按照键:', {key: value for key, value in lst1})
print('按照值:', {key: value for key, value in lst2})

output

按照键: {'a': 2, 'b': 1, 'c': 3, 'd': 0}
按照值: {'d': 0, 'b': 1, 'a': 2, 'c': 3}

多个字典排序

dict_list = [
    {'letter': 'B', 'number': '2'},
    {'letter': 'A', 'number': '3'},
    {'letter': 'B', 'number': '1'}
]

# 按 letter 排序
print(sorted(dict_list,
             key=lambda dic: dic['letter']))
# 按 letter, number 排序
print(sorted(dict_list,
             key=lambda dic: (dic['letter'], dic['number'])))

output

[{'letter': 'A', 'number': '3'}, {'letter': 'B', 'number': '2'}, {'letter': 'B', 'number': '1'}]
[{'letter': 'A', 'number': '3'}, {'letter': 'B', 'number': '1'}, {'letter': 'B', 'number': '2'}]

统计字典中元素出现的频率

借助 collections 库中的 Counter 方法

list1 = [1, 2, 3, 4, 5, 6, 8, 1, 2, 3, 6, 9, 8, 6]

from collections import Counter

print(type(Counter(list1)))
print(Counter(list1))
print(dict(Counter(list1)))

output

<class 'collections.Counter'>
Counter({6: 3, 1: 2, 2: 2, 3: 2, 8: 2, 4: 1, 5: 1, 9: 1})
{1: 2, 2: 2, 3: 2, 4: 1, 5: 1, 6: 3, 8: 2, 9: 1}

defaultdict 建立特定属性的字典

参考 python 字典defaultdict(list)

往字典 key 中添加元素或者赋值,需要先判定 key 是否在字典中

我们可以借助 defaultdict 方法,来初始化字典的 value

from collections import defaultdict

dict1 = defaultdict(int)
dict2 = defaultdict(str)
dict3 = defaultdict(list)
dict4 = defaultdict(tuple)
dict5 = defaultdict(dict)
dict6 = defaultdict(set)
dict7 = defaultdict(bool)


for i in range(1, 8):
    print("dict%s:" % str(i))
    print(type(eval("dict%s" % str(i))))  # 等价于 type(dict1)
    print(eval("dict%s[0]" % str(i)))  # 等价于 dict1[0] 

output

dict1:
<class 'collections.defaultdict'>
0
dict2:
<class 'collections.defaultdict'>

dict3:
<class 'collections.defaultdict'>
[]
dict4:
<class 'collections.defaultdict'>
()
dict5:
<class 'collections.defaultdict'>
{}
dict6:
<class 'collections.defaultdict'>
set()
dict7:
<class 'collections.defaultdict'>
False

可以看到 dict1[0] 会根据自己设定的 value 模式初始化,不会报错


下面看一下更灵活的例子,例如把相同 key 的合并到一起,value 是list

from collections import defaultdict

list1 = [("p", 1), ("p", 2), ("p", 3),
         ("q", 4), ("q", 5), ("q", 6)]

# 常规操作
dict1 = {} 
for key, value in list1:
    if key in dict1:
        dict1[key].append(value)
    else:
        dict1[key] = [value]

print("dict1:\n", dict1)

# 借助 defaultdict
dict2 = defaultdict(list)
for key, value in list1:
    dict2[key].append(value)

print("dict2:\n", dict2)
print("the value of p key:\n", dict2["p"])

output

dict1:
 {'p': [1, 2, 3], 'q': [4, 5, 6]}
dict2:
 defaultdict(<class 'list'>, {'p': [1, 2, 3], 'q': [4, 5, 6]})
the value of p key:
 [1, 2, 3]

EasyDict

EasyDict 可以让你像访问属性一样访问 dict 里的变量

from easydict import EasyDict

train = EasyDict(
    batch_size=512,
    epoch_num=160,
    workers=8,
    seed=20201223,
)
print(train["batch_size"])
print(train.batch_size)

output

512
512

可以把配置信息都写成 EasyDict 的形式,保存在 config.py 中,然后调用时

import config as cfg
cfg.train.batch_size

推导式

d = { x: x % 2 == 0 for x in range(1, 11) }
print(d)
print(type(d))

output

{1: False, 2: True, 3: False, 4: True, 5: False, 6: True, 7: False, 8: True, 9: False, 10: True}
<class 'dict'>

集合运算:字典 key 的并集, 字典 key 的并集

dic1 = {'Python': 1, 'Java': 2, 'C': 3}
dic2 = {'Python': 3, 'Java': 2, 'C++': 1}
print({k: dic1[k] for k in dic1.keys() & dic2.keys()})
print(dic1.items() & dic2.items())

output

{'Java': 2, 'Python': 1}
{('Java', 2)}

交换字典的键和值

dictionary = {"a": 1, "b": 2, "c": 3}
reversed_dictionary = {j: i for i, j in dictionary.items()}
print(reversed_dictionary)  # {1: 'a', 2: 'b', 3: 'c'}

Note: 更多连载请查看【python】

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值