字典也是序列(Sequence)。看到字典,我们能想到查字典,一本字典里的字那么多,我们通过笔画,拼音等作为依照,很快能查到一个字。python的字典也差不多,特点是查找数据特别快。
弄清楚字典之前,先要弄清楚键值对,也就是key:value的形式,比如'name':'Tom'。
其中,键(key)必须是不可变的对象,比如数字:1;2;1.1等,字符串:'name';'我爱你'等,元组:(1,);(1,2,3)等。原因是字典底层用键计算hash时,要求键不可改变,要不然就找不到数据了。
值(value)可以是任意类型的数据。
另外,字典是无序的,也就是字典没有索引,只能通过键来查找数据或者访问键所对应的数据。
目录
一,字典的创建方式:
1,使用大括号{}把键值对包起来创建
2,使用dict函数
3,使用zip()函数
4,通过dict对象的fromkeys方法
二,字典元素的访问:
1,通过键来访问
2,通过字典对象的get方法访问元素
3,列出字典所有的键值对
4,列出字典对象所有的键。
5,列出字典对象所有的值。
6,获取字典的长度和元素在不在字典里
三,字典元素的增添,修改,删除:
1,增添
2,修改
3,删除
四,字典解包及序列解包:
五,字典底层核心原理:
一,字典的创建方式:
1,使用大括号{}把键值对包起来创建
int_a = 3
float_b = 1.1
bool_tr = True
str_str = '我爱你'
def func():
print(666)
tu = (1,)
li = []
dict1 = {int_a: 12, float_b: 1, bool_tr: 2, str_str: 6, func: 12, tu: 12, "我爱你": "我爱你"}
dict2 = {li: 12}
# dict2 = {li: 12}
# TypeError: unhashable type: 'list'
# 创建dict2的时候用列表li作为键报错
由上面的代码可见函数对象也能作为键。另外,字典dict,集合set,列表list是可变对象,不能作为键。
键不能重复,值可以重复。就像查字典,读音只有一种类型,比如‘拼’的读音pin只有一个,可以通过pin来查到拼,但是查到‘拼’这个字还可以通过提手旁来查到。也就是键不能重复,值可以重复。
空字典的创建:{}里啥也不写
dict2 = {}
2,使用dict函数
2.1 用dict函数创建空字典:
d = dict()
print(d)
# {}
2.2 给dict函数传一个可迭代对象,但是这个可迭代对象要拿出两个数据,也就是必须以键值对的形式拿出来数据。例如:[(1, 2), ('name', 'tom'), ('age', 18)],[]是列表,是可迭代对象,里面是三个元组,每个元组有两个元素,恰好是键值对的形式。
也可以把[]换成(),即((1, 2), ('name', 'tom'), ('age', 18)),因为元组也是可迭代的。
d = dict([(1, 2), ('name', 'tom'), ('age', 18)])
print(d)
# {1: 2, 'name': 'tom', 'age': 18}
2.3 给dict函数传入一连串的关键字参数,关键字参数也是键值对的形式
d = dict(a=1, b=2, c=3, d='dict', bool_copy=True)
print(d)
# {'a': 1, 'b': 2, 'c': 3, 'd': 'dict', 'bool_copy': True}}
关键字参数名字遵从标识符的命名规范。
3,使用zip()函数
zip()函数又名拉链函数。
zip函数需要两个参数,这两参数都是可迭代对象,调用后返回一个zip可迭代对象(相当于生成键值对的一个算法)。
key = [1, 2, 3]
value = (1, 2, 3)
key_and_value = zip(key, value)
print(key_and_value)
d = dict(key_and_value)
print(d)
# <zip object at 0x00000274291FF848>
# {1: 1, 2: 2, 3: 3}
4,通过dict对象的fromkeys方法
参数为一个可迭代对象,对象里面的元素皆为不可变类型。创建只有键但是没有确定值的字典,值都变为None了。
key = [1, 2, 3]
d = dict.fromkeys(key)
print(d)
# {1: None, 2: None, 3: None}
简记为:剑来
二,字典元素的访问:
1,通过键来访问
语法格式为在[]里面写键。
d = {'a': 1, 'b': 2, 'c': 3, 'd': 'dict', 'bool_copy': True}
print(d['a'], d['bool_copy'])
# 1 True
2,通过字典对象的get方法访问元素
需要给get方法一个键作为参数,键存在的话返回键对应的值,否则返回None或者自定义的值。
d = {'a': 1, 'b': 2, 'c': 3, 'd': 'dict', 'bool_copy': True}
print(d.get('d'))
print(d.get('666'), '木有')
print(d['mmd']) # 没有mmd这个键就会报KeyError异常
# print(d['mmd'])
# KeyError: 'mmd'
# dict
# None 木有
3,列出字典所有的键值对
使用dict对象的items()方法。
d = {'a': 1, 'b': 2, 'c': 3, 'd': 'dict', 'bool_copy': True}
print(d.items())
print(type(d.items()))
# dict_items([('a', 1), ('b', 2), ('c', 3), ('d', 'dict'), ('bool_copy', True)])
# <class 'dict_items'>
4,列出字典对象所有的键。
使用dict对象的keys()方法。
d = {'a': 1, 'b': 2, 'c': 3, 'd': 'dict', 'bool_copy': True}
print(d.keys())
# dict_keys(['a', 'b', 'c', 'd', 'bool_copy'])
5,列出字典对象所有的值。
使用dict对象的values()方法。
d = {'a': 1, 'b': 2, 'c': 3, 'd': 'dict', 'bool_copy': True}
print(d.values())
# dict_values([1, 2, 3, 'dict', True])
6,获取字典的长度和元素在不在字典里
d = {'a': 1, 'b': 2, 'c': 3, 'd': 'dict', 'bool_copy': True}
print(len(d))
print('a' in d)
# 5
# True
只能判断键在不在字典里,值不行。
三,字典元素的增添,修改,删除:
1,增添
1,利用键来添加
如果之前不存在新加的键,则直接加上去;如果想要加的键已经存在了,则覆盖之前的。
d = {'a': 1, 'b': 2, 'c': 3, 'd': 'dict', 'bool_copy': True}
d['mmd'] = 'mmd'
d['a'] = 100
print(d)
# {'a': 100, 'b': 2, 'c': 3, 'd': 'dict', 'bool_copy': True, 'mmd': 'mmd'}
2,使用字典dict对象的update方法,需要给update方法传入一个字典作为参数,也就是update(字典对象)。
d = {'a': 1, 'b': 2, 'c': 3, 'd': 'dict', 'bool_copy': True}
d1 = {'q': 'q', 'a': 100}
d.update(d1)
print(d)
# {'a': 100, 'b': 2, 'c': 3, 'd': 'dict', 'bool_copy': True, 'q': 'q'}
同样的,如果之前不存在新加的键,则直接加上去;如果想要加的键已经存在了,则覆盖之前的。
2,修改
1,使用键来修改
d = {'a': 1, 'b': 2, 'c': 3, 'd': 'dict', 'bool_copy': True}
d['a'] = 100
print(d)
# {'a': 100, 'b': 2, 'c': 3, 'd': 'dict', 'bool_copy': True}
2,使用update方法来覆盖
3,删除
1,del语句
d = {'a': 1, 'b': 2, 'c': 3, 'd': 'dict', 'bool_copy': True}
del d['a']
del(d['b'])
print(d)
# {'c': 3, 'd': 'dict', 'bool_copy': True}
2,使用dict对象的pop方法,把键作为参数。
d = {'a': 1, 'b': 2, 'c': 3, 'd': 'dict', 'bool_copy': True}
d.pop('a')
print(d)
# {'b': 2, 'c': 3, 'd': 'dict', 'bool_copy': True}
3,使用dict对象的clear方法,清空字典。
d = {'a': 1, 'b': 2, 'c': 3, 'd': 'dict', 'bool_copy': True}
print(d)
# {'a': 1, 'b': 2, 'c': 3, 'd': 'dict', 'bool_copy': True}
d.clear()
print(d)
# {}
4,使用dict对象的popitem方法,随机删除一个键值对(因为字典是无序的,所以随机)
d = {'a': 1, 'b': 2, 'c': 3, 'd': 'dict', 'bool_copy': True}
d.popitem()
print(d)
# {'a': 1, 'b': 2, 'c': 3, 'd': 'dict'}
d.popitem()
print(d)
# {'a': 1, 'b': 2, 'c': 3}
d.popitem()
print(d)
# {'a': 1, 'b': 2}
四,字典解包及序列解包:
将多个值赋给一个变量时,Python会自动将多个值封装成元组,这个功能就称为序列封包。
反过来,序列解包可以为多个变量赋值。要求接收值得变量个数与解包序列里面的数据个数相同,会逐个给变量赋值。
列表list,元组tuple,字典dict,enumerate枚举对象,filter对象,range对象都可以解包。
x, y, z = (1, 2, 3)
a, b, c = 1, 2, 3
(d, e, f) = 1, 2, 3
[m,n,p] = [1,2,3]
def func():
return 1, 2, 3
g, h, i = func()
print(x, y, z)
print(a, b, c)
print(d, e, f)
print(g, h, i)
# 1 2 3
# 1 2 3
# 1 2 3
# 1 2 3
字典解包时是利用字典的键进行处理。
d = {'name': 'tom', 'age': 18}
a, b = d
print(a, b)
# name age
如果要利用字典的值进行处理的话,则需要用dict对象的values方法。dict.values()返回一个dict_values对象,这个对象与列表类似。
d = {1: 1, 2: 2}
print(d.values())
# dict_values([1, 2])
如果要对键值对进行处理的话,则需要用dict对象的items方法。dict.items()方法返回一个元素为二元组的序列,也就是dict_items对象。
d = {1: 1, 2: 2}
print(d.items())
# dict_items([(1, 1), (2, 2)]),列表里面有两个元组,每个元组有两个数据,称为二元组
另外也可以用运算符‘*’来解包元组,也就是把元组()给解开不要括号。函数调用时的不定长位置参数就是这样的。
def func(*args):
print(args)
print(*args)
# (1, 2, 3, 4)
# 1 2 3 4
func(1, 2, 3, 4)
# 这里把1,2,3,4都传给了args,属于把多个数据赋值给一个变量,实际就是封包。
# args就是一个元组,用*来解包。
另外也可以用运算符‘**’来解包字典,从而去掉{}。但是解包后仍然是一个键值对,仍需要用{}包起来。
print(*[1, 2, 3], 4, *(5, 6))
print(*range(4), 4)
print({*range(4), 4, *(5, 6, 7)})
print({'x': 1, **{'y': 2}}) # 看这里**解包字典
# 1 2 3 4 5 6
# 0 1 2 3 4
# {0, 1, 2, 3, 4, 5, 6, 7}
# {'x': 1, 'y': 2}
dict1 = {1: 2, 2: 3}
print({**dict1})
# {1: 2, 2: 3}
def func(**kwargs):
print(kwargs)
print(type(kwargs))
print(kwargs.items())
# {'a': 1, 'b': 2, 'c': 3}
# <class 'dict'>
# dict_items([('a', 1), ('b', 2), ('c', 3)])
func(a=1, b=2, c=3)
当接受的变量个数与要解包序列里面的数据个数不一样时,需要像下面这样操作。
a, b, *c = range(10)
print(a, b, c, type(c))
m, n, k, *d, e, f = range(10) # *d会自动确定数量,相当于可变位置参数的意思
print(m, n, k, d, e, f)
# 0 1 [2, 3, 4, 5, 6, 7, 8, 9] <class 'list'>
# 0 1 2 [3, 4, 5, 6, 7] 8 9
类似于函数形参,*var通吃多个位置实参。不同之处在于,var类型是list而不是元组。这里的var代表变量名。
五,字典底层核心原理:
字典对象的核心是散列表。散列表是一个稀疏数组,也就是一个数组里边存在没有数据的位置,即给他分配了内存块空间,但是这个内存块空间里没有数据。这个空间也称为bucket,bucket内存块分为两部分,一个是键对象的引用,一个是值的引用。换句话说,一个键值对在内存中占的空间就叫做bucket。
字典储存数据时,先用内置函数(builtin functions)的hash()函数计算键的散列值,再用bin()函数将计算得出的散列值转为二进制。利用字典底层数组长度(长度一般为2^n次个)来确定偏移的位置(利用的是n),确定偏移位置后,把数据存在那里,但实际上是存的数据对象的id地址)。
访问字典元素时,也是通过散列值去找数据。如果键不存在的话,也就是访问到了空的内存块,则会报KeyError异常。