前置疑问(对所学的疑问自己想知道什么)
Q1 字典有什么特点?
Q2 什么时候需要用到字典数据类型?
Q3 字典好像有键值对,具体有什么什么样子的?
Q4 字典如何在内存中存储的呢?是一块内存中分为两部分,一部分存储key,一部分存储value?
Q5 集合有并、交、差操作吗?
学习内容
1、字典的特点、创建、常见的操作方法、核心底层原理
2、集合的创建、常见的操作方法
学习时突发疑问
Q6 一个key 能对应多个value吗?一个value 能对应多个key吗?
我猜测key一对多value不行,但是多个键可以有同一个值,有点像数学中的函数 x与y的关系。可是意义不大啊。比如name 和 age 这个值明显不一样嘛。
Q7 满足什么条件才能作为键?
学习产出
1、字典
1.1 什么是字典
A1
字典是一个键/值对的集合,是无序的,且可变的。
1.2 创建字典的方式
A3
- 基本语法创建{}
{key:value}
>>> a = {'name':'xiaoming','age':20,'job':'programmer'} #
>>> a
{'name': 'xiaoming', 'age': 20, 'job': 'programmer'}
>>> type(a)
<class 'dict'>
- dict()方法创建
>>> b = dict(name = 'xiaoming', age=18, job = 'programmer')
>>> b
{'name': 'xiaoming', 'age': 18, 'job': 'programmer'}
>>> type(b)
<class 'dict'>
- zip()方法创建
>>> k = ['name','age','job']
>>> v = ['xiaoing',18,'programmer']
>>> c = dict(zip(k,v))
>>> c
{'name': 'xiaoing', 'age': 18, 'job': 'programmer'}
>>> type(c)
<class 'dict'>
- fromkeys()方法创建
>>> d = dict.fromkeys(['name','age','job'])
>>> d
{'name': None, 'age': None, 'job': None}
>>> type(d)
<class 'dict'
还是{} 和dict() 方法创建简单
1.3 常见操作
1.3.1 访问
- 通过键获得值,若键不存在,则报错
>>> a = {'name':'xiaoming','age':20,'job':'programmer'}
>>> a
{'name': 'xiaoming', 'age': 20, 'job': 'programmer'}
>>> a['name']
'xiaoming'
>>> a['id']
Traceback (most recent call last):
File "<pyshell#35>", line 1, in <module>
a['id']
KeyError: 'id'
- 通过get()方法来获得值
>>> a.get('name')
'xiaoming'
>>> a.get('age')
20
>>> a.get('id') #不存在的i键 返回None
>>> a.get('id', 1234567) #可以指定返回内容
1234567
- 列出所有的键值对
>>> a.items()
dict_items([('name', 'xiaoming'), ('age', 20), ('job', 'programmer')])
- 列出所有的键、列出所有的值
>>> a.keys()
dict_keys(['name', 'age', 'job'])
>>> a.values()
dict_values(['xiaoming', 20, 'programmer'])
- 键值个数(字典长度)
>>> a.keys()
dict_keys(['name', 'age', 'job'])
>>> len(a)
3
- 检测键是否存在字典中
>>> 'id' in a
False
>>> 'name' in a
True
1.3.2 增加、修改、删除
- 直接添加
>>> a['id'] = 12345678
>>> a
{'name': 'xiaoming', 'age': 20, 'job': 'programmer', 'id': 12345678}
- 用update()方法更新原来的字典对象
>>> b = {'name': 'xiaoming', 'age': 20, 'job': 'programmer', 'id': 12345678, 'sex': 'man'}
>>>> a.update(b)
>>> a
{'name': 'xiaoming', 'age': 20, 'job': 'programmer', 'id': 12345678, 'sex': 'man'}
- del()方法删除
>>> del(a['sex'])
>>> a
{'name': 'xiaoming', 'age': 20, 'job': 'programmer', 'id': 12345678}
- pop()方法删除指定键值对
>>> a.pop('id')
12345678
>>> a
{'name': 'xiaoming', 'age': 20, 'job': 'programmer'}
- popitem()方法随机删除键值对
因为字典是无序的
>>> a.popitem()
('test2', 'test2')
>>> a
{'name': 'xiaoming', 'age': 20, 'job': 'programmer', 'test1': 'test1'}
- clear()方法清除所有的键值对
>>> a1 = a
>>> a1
{'name': 'xiaoming', 'age': 20, 'job': 'programmer', 'test1': 'test1'}
>>> id(a1)
2239422203328
>>> id(a)
2239422203328
>>> a1.clear()
>>> a1
{}
>>> a
{}
1.3.3 序列解包 :a,b,c = x,y,z 这种格式
- 键(默认) 返回的全是键
>>> a = {'name': 'xiaoming', 'age': 20, 'job': 'programmer'}
>>> a
{'name': 'xiaoming', 'age': 20, 'job': 'programmer'}
>>> name, age, job = a
>>> name
'name'
>>> age
'age'
>>> job
'job
- 值 values() 返回的全是值
>>> name, age, job = a.values()
>>> name
'xiaoming'
>>> age
20
>>> job
'programmer'
键值对 items() 以键值对返回
>>> name, age, job = a.items()
>>> name
('name', 'xiaoming')
>>> age
('age', 20)
>>> job
('job', 'programmer')
>>> type(name) #注意返回的类型是元组
<class 'tuple'>
>>> type(age)
<class 'tuple'>
>>> type(job)
<class 'tuple'>
1.4 核心底层原理
A4
1.4.1 简述过程
字典对象的核心散列表(稀疏数组),数组的每个单元 叫做一个bucket,bucket 分为两部分 一个是key对象的地址,一个是值对象的地址。通过hash()方法存键取值,具体细节部分,如下。
1.4.2 存的过程
存的过程主要解决如何让这个key 放到对应的bucket索引中去,如图:
过程如下:
首先用hash()计算出key的散列值(二进制表示),然后从右往左依次取k位二进制,将这k位二进制转为十进制,记为i,如果i对应的bucket为空,则存放;如果不为空,再次向左侧取k位二进制。如果bucket满了,超过2/3时会自动扩容。
如何计算这个k呢?bucket为8时,k为3,bucket为16时,k为4。依次类推。
>>> a
{'name': 'xiaoming', 'age': 20, 'job': 'programmer'}
>>> bin(hash("name")) #计算出name的散列值,以二进制表示
'-0b1111001100101101011101101100100001110010100000110100011100110'
流程图如下:
那就有个疑问了,bucket的空间要是不是 2^k 怎么办呢? 还是说bucket默认创建的空间就是2^k 。会不会太浪费内存了呢?
我猜测这个内存默认开辟空间为2^k大小,且是根据实际自己电脑具体位数,来计算的。
比如我里面只有2个键值对,实际能存可能是能存放8个,如果是9个键值对,实际开辟的大小是16。
这字典是以空间换时间。读取数据快。
1.4.3 根据键取值的过程
当我们用get(“name”)找到对应的值时,首先还是会计算这个键的散列值,然后依然是从右向左取k位二进制数,转为十进制数位i,如果i位空,返回None;如果不为空,需要计算对应此时已经存储i索引的键的散列值,与当前的对象进行判断,如果相等,取出值;如果不相等,则向左再取k个二进制,进行计算,直到结束。
如图所示:
流程图:
A6
a = {"name":"xiaoming","name":"xiaohong","age":20}
>>> a
{'name': 'xiaohong', 'age': 20} # 键名称为name的只有一个。满足集合的特点。
A7
键必须是可散列的。
满足两个条件称为"可散列":
1、具有__hash__方法 ,可将一个类型映射为一个int值,且在一个生命周期中保持不变。
2、具有__eq__方法,能够判断两个对象是否相等。
2、集合
2.1 创建字典的方式
- 基础语法创建{}
>>> a = {1,2,3,4,5,5,5,5}
>>> a
{1, 2, 3, 4, 5}
- set()方法创建
可将列表、元组转为集合
>>> a = set([1,2,3,4,5,5,5])
>>> a
{1, 2, 3, 4, 5}
>>> a = set((1,2,3,4,5,6,6,7,7,7))
>>> a
{1, 2, 3, 4, 5, 6, 7}
2.2 增 、删、并、交、差操作
- 增 add()
>>> a
{1, 2, 3, 4, 5, 6, 7}
>>>
>>> a.add(8)
>>> a
{1, 2, 3, 4, 5, 6, 7, 8}
- 删 remove(), clear()
>>> a
{1, 2, 3, 4, 5, 6, 7, 8}
>>> a.remove(1)
>>> a
{2, 3, 4, 5, 6, 7, 8}
>>> a.clear()
>>> a
set()
A5
- 并(|)union()、交(&)intersection()、差(-)difference()
>>> a = {1,2,3}
>>> b = {4,5,6}
>>> c = {1,2,3,4,5,6}
>>> a | b
{1, 2, 3, 4, 5, 6}
>>> a & b
set()
>>> a - b
{1, 2, 3}
>>> b - a
{4, 5, 6}
>>> a & c
{1, 2, 3}
>>> b & c
{4, 5, 6}
>>> a.union(b)
{1, 2, 3, 4, 5, 6}
>>> a.intersection(b)
set()
>>> a.difference(b)
{1, 2, 3}
2.3 集合的特点
无序、无重复、可变
3 列表、元组、字典、集合的区别
数据类型 | 有序无序 | 是否可变 | 各自特点 |
---|---|---|---|
列表 | 有序 | 可变 | 像是升级的顺序表 |
元组 | 有序 | 不可变 | 受到限制的列表 |
字典 | 无序 | 可变 | 键值对,是特殊的集合,无重复元素 |
集合 | 无序 | 可变 | 只有键,无值,无重复元素 |
今日代码
>>> a = [
['xiaoming', 20, '20330801', 'm'],
['xiaohong', 20, '20300802', 'f'],
['xiaogang', 20, '20300803', 'm'],
]
>>> a
[['xiaoming', 20, '20330801', 'm'], ['xiaohong', 20, '20300802', 'f'], ['xiaogang', 20, '20300803', 'm']]
>>> d1 = {"name":"xiaoming","age":20,"salary":30000,"location":"beijing"}
>>> d2 = {"name":"xiaohong","age":20,"salary":20000,"location":"shanghai"}
>>> d3 = {"name":"xiaogang","age":20,"salary":15000,"location":"hangzhou"}
>>>
>>> td = [d1,d2,d3]
>>> td
[{'name': 'xiaoming', 'age': 20, 'salary': 30000, 'location': 'beijing'}, {'name': 'xiaohong', 'age': 20, 'salary': 20000, 'location': 'shanghai'}, {'name': 'xiaogang', 'age': 20, 'salary': 15000, 'location': 'hangzhou'}]