Fork me on GitHub

数据类型之字典

一、概要

  • 字典基础
  • 字典基本操作
  • 字典方法
  • 字典总结

二、字典基础

(一)创建字典

一个字典通过一对大括号进行创建,键值之间使用(:)进行分隔,每一对键值之间使用(,)分隔,如果大括号中无值那么就是空字典;它是Python中唯一具有映射关系的数据类型,效率高于列表。

 d = {"name":"bright",12:["jskfj",23]}  #键唯一且为不可变数据类型

当然如果键值不唯一,也不会抛出异常,只不过相同的键会被覆盖掉。

d = {"name":"bright",12:["jskfj",23],"name":"gh"}  #键唯一且为不可变数据类型
print(d)

#-----------------------------------------输出———————————————
{12: ['jskfj', 23], 'name': 'gh'}

(二)dict函数

可以使用dict函数通过其它映射进行字典的创建

items=[('name','张三'),['iphone',1872563541]]
d=dict(items)
print(d) #{'name': '张三', 'iphone': 1872563541}

可以看出,为dict函数传入一个列表,而列表的元素就是一个个的列表或者元组,元素中包含两个值,第一个值表示了字典的键,第二个值表示字典的值。

另外dict也可以通过关键字传参,创建字典。

d=dict(name='张三',iphone=1872563541)
print(d) #{'iphone': 1872563541, 'name': '张三'}

如果dict函数不指定任何参数,就会创建一个空字典

d=dict()
print(d)#{}

三、字典基本操作

(一)len(dict)

返回字典中元素(键值对)的个数

d = {"name":"bright",12:["jskfj",23],"name":"gh"}
print(len(d))#2

(二) dict[key]

#获取键对应的值
d = {"name":"bright",12:["jskfj",23],"name":"gh"}
result=d["name"]
print(result)#gh

#对键进行赋值
d = {"name":"bright",12:["jskfj",23],"name":"gh"}
d["name"]="root"
print(d)#{12: ['jskfj', 23], 'name': 'root'}

(三) del dict[key]

d = {"name":"bright",12:["jskfj",23],"name":"gh"}
del d["name"]
print(d)#{12: ['jskfj', 23]}

(四) key in dict

判断dict中是否含有键为key的项

d = {"name":"bright",12:["jskfj",23],"name":"gh"}
print("name" in d)#True

(五) 字典格式化字符串

在字符串的格式化中,已经有很多方法了,这里也可以通过字典进行格式化,引进format_map()方法。

定义格式化参数字典:

data={
    "name":"bright",
    "age":27,
    "hobby":"music"
}

定义字符串模板:

str="""

{name} is a boy,{age} years old,liking {hobby}.

"""

使用format_map()方法格式化字符串:

print(str.format_map(data))#bright is a boy,27 years old,liking music.

四、字典方法

(一) clear()

清空字典中的所有元素

d = {"name":"bright",12:["jskfj",23],"name":"gh"}
d.clear()
print(d)#{}

(二) copy()与deepcopy()

copy()方法复制的字典只是浅复制,复制的是第一层的字典数据。也就是说修改原字典还是新字典,对应的元素都会改变。

d = {"name":"bright",12:["jskfj",23]}
d1=d.copy()
print(d1)
print(d)

############输出###########################
    {12: ['jskfj', 23], 'name': 'bright'}
    {12: ['jskfj', 23], 'name': 'bright'}

修改原字典中的值:

d = {"name":"bright",12:["jskfj",23]}
d1=d.copy()
d["name"]="alb"
print(d1)
print(d)

#################输出################
    {12: ['jskfj', 23], 'name': 'bright'}
    {12: ['jskfj', 23], 'name': 'alb'}

修改copy后新字典中的值:

d = {"name":"bright",12:["jskfj",23]}
d1=d.copy()
d1["name"]="sjp"
print(d1)
print(d)

###############输出###################
{12: ['jskfj', 23], 'name': 'sjp'}
{12: ['jskfj', 23], 'name': 'bright'}

修改第二层数据:

d = {"name":"bright",12:["jskfj",23]}
d1=d.copy()
d[12][0]=56
print(d)
print(d1)

################输出###############
{12: [56, 23], 'name': 'bright'}
{12: [56, 23], 'name': 'bright'}

从上面可以看到修改第二层数据,无论是修改新字典还是原字典,另外一个字典的值都会改变,如果想改变这种情况,就需要进行深拷贝。

from copy import deepcopy
d = {"name":"bright",12:["jskfj",23]}
d1=deepcopy(d)
d[12][0]=78
print(d)
print(d1)
###################输出##################
    {12: [78, 23], 'name': 'bright'}
    {12: ['jskfj', 23], 'name': 'bright'}

(三) fromkeys()

用于根据key建立新的字典,该方法的返回值就是新的字典。在新的字典中所有的key都有相同的默认值None,不过可以根据fromkeys方法的第二个参数进行指定默认值。

#在新字典上调用fromkeys创建新的字典,通过列表指定key
dic={}.fromkeys(['name','age','hobby'])
print(dic) #'hobby': None, 'age': None, 'name': None}

#通过元组指定key
dic={}.fromkeys(('name','age','hobby'))
print(dic) #'hobby': None, 'age': None, 'name': None}

通过fromkeys的第二个参数指定默认值

dic={}.fromkeys(('name','age','hobby'),'bx')
print(dic) #{'hobby': 'bx', 'age': 'bx', 'name': 'bx'}

(四) get()

获取对应key的value,使用dict[key]可以获取值,但是一旦key不存在就会抛出异常,get()方法就不会抛出异常,如果key不存在,会返回None值。当然也可以通过get的第二个参数指定传入的默认值。

d = {"name":"bright",12:["jskfj",23]}
print(d.get("name"))#bright
print(d.get("age"))#None

(五) items()与keys()

在字典的基本操作中已经说明了这两种方法,items方法用于获取字典的key-value,返回值是一个可迭代的dict_items类型;keys方法用于返回字典中所有的key,是dict_keys类型,也是可迭代的。

d = {"name":"bright",12:["jskfj",23]}
result1=d.items()
result2=d.keys()
print(type(result1))
for key,val in result1:
    print(key,val)
print(type(d.keys()))
for key in result2:
    print(key)
    
#########输出############
<class 'dict_items'>
12 ['jskfj', 23]
name bright

<class 'dict_keys'>
12
name

(六) pop()和popitem()

pop方法和popitem方法用于弹出字典中的元素,pop方法用于获取指定的key的值,并从字典中弹出这个key-value,popitem方法用于返回最后一个key-value,并且弹出这对key-value。

#pop方法dict={'name':'xiaoli','depart':'xiaoshou','age':27}
print(dict.pop('name'))#xiaoli
print(dict)#{'age': 27, 'depart': 'xiaoshou'}

#popitem方法
dict={'name':'xiaoli','depart':'xiaoshou','age':27}
print(dict.popitem())#('age', 27)
print(dict)#{'name': 'xiaoli', 'depart': 'xiaoshou'}

(七) setdefault()

setdefault方法用于设置key的默认值,该方法接收两个参数,第一个表示key,第二个表示默认值,如果key在字典中不存在,那么就会向字典中添加key,并用第二个参数作为值(参数未指定就为None),如果字典中已经存在key,setdefault不会修改key原来的值,而且该方法会返回原来的值。

dict={}
dict.setdefault("name","menb")
dict.setdefault("name","niko")
dict.setdefault("age")
print(dict)

##############输出########
{'age': None, 'name': 'menb'}

其实这个方法与dict[key]=value差不多,但是这个方法可以添加元素,但是不能修改元素,因为已经存在的key,它只会返回不会修改,而dict[key]=value既可以添加又可以修改。

(八) update()

update方法用于一个字典中的元素更新另外一个字典。该方法接收一个参数,该参数表示用作更新数据的字典的数据源。列如:

d1.update(d2)

将d2中的元素更新到d1中,如果d2中的key-value对在d1中不存在,就会在d1中新添加key-value对,如果d2中的key在d1中已经存在,那么就会把d2中的key的value更新给d1中key的value。

d1={"name":"bright","age":27,"hobby":"music"}
d2={"name":"sansha","city":"陕西"}
d1.update(d2)
print(d1)#{'city': '陕西', 'hobby': 'music', 'age': 27, 'name': 'sansha'}

该方法试讲两个列表进行合并,当然还有其它方法,使用dict(dict1,**dict2):

>>> dict1 = {'name':'bright','age':28}
>>> dict2 = {'identified':'worker'}
>>> dict(dict1,**dict2)
{'age': 28, 'name': 'bright', 'identified': 'worker'}

(九) values()

获取字典中的值,返回的是dict_values类型,是可迭代的。它与keys方法返回的有一点不一样就是,它返回的值得列表可以有重复的,而keys()方法返回的都是唯一值的列表。

dict={"name":"bright","age":27,"hobby":"music"}
result=dict.values()
print(type(result))
for val in result:
    print(val)
##########输出###########
    <class 'dict_values'>
    music
    27
    bright

五、字典总结

对字典操作最多的无非就是增、删、查、改这些基本操作,现在就对这些基本操作总结一下。

 d = {"name":"bright",12:["jskfj",23]}

# #查
 print(d["name"])
 print(d.get("name",None)) #推荐

##增加
# d["age"] = "123"
# d.default("age","123")
 print(d)

#修改
# d["name"] = "zhangfei"

#删除
# ret = d.pop(12)
# del d[12]
# print(ret)

##更新
 d1 = {"name":"alia",12:["jskfj",23]}
 d2 =  {"name":"megon",12:["jskfj",23],"age":32}
 d2.update(d)
 print(d2)

注意:

字典的键可以是任意不可变类型,例如,元组、字符串等。

六、字典内存模型

画图

七、有序字典

如何解决字典有序问题 python3.6之后是有序

如果之前的版本使用OrderDict使其有序:

from collections import OrderedDict
d=OrderedDict()
d['foo']=1
d['bar']=2
d['spam']=3
d['grok']=4
for key in d:
  print(key,d[key])

八、字典的键为什么不可变

首先看一个例子:

dict1 = {"key1":"val1", 1:"val2",[1,2,3]:"val3"}

请问上面这个字典有什么问题吗?

显然会有问题,因为列表时可变的,无法哈希的。

这与字典底层的存储是分隔不开的,列表底层使用的是线性表,是一块连续的内存空间,比如:

list1 = [1,2,3] # 通过索引取值,复杂度为o(1);查找具体的值是o(n)

如果需要查找3,在不知道索引的情况下,就通过遍历列表的元素进行比较,复杂度为o(n)。那么字典是如何在不通过遍历的情况下找到对应元素呢?

d1 = {1:"a", 2:"b", 3:"c"}

如上在d1中查找最后一个一个键值对。

首先它需要将上面的数据进行存储,它申请了一块空间,类似就是列表或者数组一样的。

1 % 3 = 1
2 % 3 = 2
3 % 3 = 0

等到取值时,就可以通过上面的计算方式可以得到对应值在这个表中的下标,然后取值。

而上面的表就是哈希表,那么什么时哈希表呢?

哈希表是基于键、值对存储的数据结构,底层一般采用的是列表(数组)。

而基于键计算的下标的过程就是通过哈希函数来实现的。

哈希函数的功能:提供把关键字映射到列表中的位置算法,是哈希表存储数据的核心所在。

哈希算法决定了数据的最终存储位置,不同的哈希算法设计方案,也关乎哈希表的整体性能,所以,哈希算法就变得的尤为重要。

那么,如果字典是这样的话:

d1 = {'ab':1, 'bcd':2, 'ca':3}

# 简单哈希函数
a = 1
b = 2
...
z = 26

ab = 1 + 2 = 3
bcd = 2 + 3 + 4 = 9
ca = 3 + 1 = 4

# 计算位置 对个数取余
3 % 3 = 0
9 % 3 = 0
4 % 3 = 1
# 之后与上面的一样

实际上哈希函数的算法有很多。

实例:

def hash_code(key):
    # 设置字母 A 的在字母表中的位置是 1
    a = 1
    b = 2
    c = 3
    if key == 'ab':
        res = a + b
    elif key == 'ac':
        res = a + c
    else:
        res = b + c

    return res % 3


hash_table = [None] * 3

# 保存所有学生
idx = hash_code('ab')
hash_table[idx] = '张三'
idx = hash_code('bc')
hash_table[idx] = '李四'
idx = hash_code('ac')
hash_table[idx] = '王五'
print('哈希表中的数据:', hash_table)

另外Python中有内置的hash函数,专门将字符串转成整数。而这个hash的过程就是不可变的,因为下标不能变动,否则取值就不正确。

posted @ 2023-01-19 16:56  iveBoy  阅读(4)  评论(0)    收藏  举报
TOP