列表的优点在于批量处理数据,不擅长精确定位,所以需要使用字典类型

迭代器以时间换空间;
字典以增加内存空间,换减少检索时间
image.png

字典也有生成式,却拿空间换时间 - 图2

dict() 字典对象构造函数

  1. with open('NYSE_20191220.txt', 'r') as file:
  2. data = file.readlines()
  3. data = [s.split(',') for s in data]
  4. data = [[s[0], int(s[6].strip())] for s in data]
  5. data[:10]
  1. [['A', 2287800],
  2. ['AA', 5139800],
  3. ['AAN', 1010200],
  4. ['AAP', 1269100],
  5. ['AAT', 675800],
  6. ['AB', 211400],
  7. ['ABB', 2686400],
  8. ['ABBV', 19135600],
  9. ['ABC', 1666800],
  10. ['ABEV', 8535000]]
  1. dict(data[:10])
  1. {'A': 2287800,
  2. 'AA': 5139800,
  3. 'AAN': 1010200,
  4. 'AAP': 1269100,
  5. 'AAT': 675800,
  6. 'AB': 211400,
  7. 'ABB': 2686400,
  8. 'ABBV': 19135600,
  9. 'ABC': 1666800,
  10. 'ABEV': 8535000}

频次统计:dict() + zip()

  1. a = ['A', 'B', 'C', 'D']
  2. b = [2, 3, 5, 7]
  3. c = zip(a, b)
  4. d = dict(c)
  5. d
  1. {'A': 2, 'B': 3, 'C': 5, 'D': 7}
  1. a = ['张三','李四','张三','王五','张三','李四','张三','王五','王五','张三','赵六','赵六']
  2. b = [a.count(x) for x in a]
  3. d = dict(zip(a, b))
  4. d
  1. {'张三': 5, '李四': 2, '王五': 3, '赵六': 2}

字典特性:字典在创建时会自动去除重复键,所以最后只会保留一个“张三”的记录

字典对象的 fromkeys 方法

  1. d = {}.fromkeys(a)

不是内置函数,所以不能直接写 d = fromkeys(a)
而是要由某个字典对象调用,比如d = 字典.fromkeys(a)

通常写法:

  1. d = {}.fromkeys(列表)

以列表中每个元素为一个键向字典中添加键值对,然后将新字典对象返回(赋值给d)

标准用法:

  1. d = {}.fromkeys(列表,默认值)

将参数列表(或其他可送代对象)中的每个元素作为一个键,对应值为参数默认值,创建字典(并赋值给变量d)。
常用来创建一个初始字典,然后再使用循环等结构,修改每个键对应的值

频次统计

  1. a = ['张三','李四','张三','王五','张三','李四','张三','王五','王五','张三','赵六','赵六']
  2. d = {}.fromkeys(a, 0)
  3. for k in d:
  4. d[k] = a.count(k)
  5. d
  1. {'张三': 5, '李四': 2, '王五': 3, '赵六': 2}

【注意】

  1. fromkeys不会修改原字典,只会创建新字典
  2. 不会转换二维列表
  3. 不会合并二个列表
  4. 不要使用可变对象(如列表)作为默认值
  1. a = ['A', 'B', 'C', 'D']
  2. b = [2, 3, 5, 7]
  3. d = {}.fromkeys(a, b)
  4. d
  1. {'A': [2, 3, 5, 7], 'B': [2, 3, 5, 7], 'C': [2, 3, 5, 7], 'D': [2, 3, 5, 7]}
  1. d['A'][0] = 'a'
  2. d
  1. {'A': ['a', 3, 5, 7],
  2. 'B': ['a', 3, 5, 7],
  3. 'C': ['a', 3, 5, 7],
  4. 'D': ['a', 3, 5, 7]}

这三个元素(键值对)中的值[1,2,3]都是同一块内存区域、同一个列表。所以一改全改!
d[‘A’] 是字典d中A对应的值,即列表[1,2,3]。
因此修改 d[‘A’][0] 就是修改[1,2,3]中的第一个元素。

字典生成式

  1. 从for语句变换而来,把最终存字典的键和值放到前面,中间用冒号连接,然后把循环规则放到后面。
  2. 注意生成式要使用花括号。
  3. 如果有if,放到for后面。
  1. with open('NYSE_20191220.txt', 'r') as file:
  2. data = file.readlines()
  3. data = [s.split(',') for s in data]
  4. data = data[:10]
  5. d = {}
  6. for x in data:
  7. d[x[0]] = x[5]
  8. d
  1. {'A': '85.19',
  2. 'AA': '21.23',
  3. 'AAN': '58.86',
  4. 'AAP': '158.12',
  5. 'AAT': '44.95',
  6. 'AB': '29.98',
  7. 'ABB': '23.84',
  8. 'ABBV': '89.29',
  9. 'ABC': '85.67',
  10. 'ABEV': '4.57'}
  1. d = {x[0]:x[5] for x in data}
  2. d
  1. {'A': '85.19',
  2. 'AA': '21.23',
  3. 'AAN': '58.86',
  4. 'AAP': '158.12',
  5. 'AAT': '44.95',
  6. 'AB': '29.98',
  7. 'ABB': '23.84',
  8. 'ABBV': '89.29',
  9. 'ABC': '85.67',
  10. 'ABEV': '4.57'}

频次统计

  1. a = ['张三','李四','张三','王五','张三','李四','张三','王五','王五','张三','赵六','赵六']
  2. d = {i:a.count(i) for i in a}
  3. d
  1. {'张三': 5, '李四': 2, '王五': 3, '赵六': 2}
  1. d = {i:a.count(i) for i in a if a.count(i) > 3}
  2. d
  1. {'张三': 5}
  1. a = ['英国', '法国', '俄罗斯']
  2. b = ['UK', 'FRA3', 'RUS']
  3. d = {x[0]:x[1] for x in zip(a, b)}
  4. d
  1. {'英国': 'UK', '法国': 'FRA3', '俄罗斯': 'RUS'}

字典生成式不等价于for循环

  1. a = ['A', 'B', 'A', 'C', 'B', 'A']
  2. d = {}.fromkeys(a, 0)
  3. for k in a:
  4. d[k] = d[k] + 1
  5. print(d)
  1. {'A': 3, 'B': 2, 'C': 1}
  1. a = ['A', 'B', 'A', 'C', 'B', 'A']
  2. d = {}.fromkeys(a, 0)
  3. d = {k:d[k] + 1 for k in d}
  4. print(d)
  1. {'A': 1, 'B': 1, 'C': 1}

这个例子尝试统计列表 a 中各个字符的出现次数,并将结果存入字典。
但是左边 for 循环的模式可以正常完成任务,而右边同样语法的生成式却会计算出错。
原因就在于:生成式里用到的字典 d ,并不是随时添加新元素的那个新字典,而是前一行定义的那个默认值全是 0 的字典。
image.png

练习

练习1:字典创建技巧

请在Python交互式命令窗口中完成以下操作:

创建两个列表并赋值给 a 和 b 两个变量,内容分别为足球世界杯历届举办年份及冠军,即:
a = [ 1994, 1998, 2002, 2006, 2010, 2014, 2018 ]
b = [ ‘巴西’, ‘法国’, ‘巴西’, ‘意大利’, ‘西班牙’, ‘德国’, ‘法国’ ]
注意:创建列表 a 时请尽量不要直接从本作业中复制内容,而是思考是否有更加简洁的写法!

思考并试验:怎样使用 dict() 的方式,基于 a 和 b 两个列表制作字典:{ 1994:’巴西’, 1998:’法国’ , …… } ?

思考并试验:怎样使用字典生成式,基于 a 和 b 两个列表制作字典:{ 1994:’巴西’, 1998:’法国’ , …… } ?

思考:能否使用 fromkeys() 的方式,基于 a 和 b 两个列表制作字典:{ 1994:’巴西’, 1998:’法国’ , …… } ?如果不能、原因是什么?如果能,应该怎样设计这个代码?

  1. a = list(range(1994, 2019, 4))
  2. b = [ '巴西', '法国', '巴西', '意大利', '西班牙', '德国', '法国' ]
  3. d = dict(zip(a, b))
  4. print(d)
  1. {1994: '巴西', 1998: '法国', 2002: '巴西', 2006: '意大利', 2010: '西班牙', 2014: '德国', 2018: '法国'}
  1. a = list(range(1994, 2019, 4))
  2. b = [ '巴西', '法国', '巴西', '意大利', '西班牙', '德国', '法国' ]
  3. d = {a[i]:b[i] for i in range(len(a))}
  4. print(d)
  1. {1994: '巴西', 1998: '法国', 2002: '巴西', 2006: '意大利', 2010: '西班牙', 2014: '德国', 2018: '法国'}
  1. a = list(range(1994, 2019, 4))
  2. b = [ '巴西', '法国', '巴西', '意大利', '西班牙', '德国', '法国' ]
  3. d = {}.fromkeys(a)
  4. for i, k in enumerate(d):
  5. d[k] = b[i]
  6. print(d)
  1. {1994: '巴西', 1998: '法国', 2002: '巴西', 2006: '意大利', 2010: '西班牙', 2014: '德国', 2018: '法国'}
  1. d2 = {}.fromkeys( a )
  2. for i in d2:
  3. d2[ i ] = b[ (i-1994)//4 ]
  4. print(d2)
  1. {1994: '巴西', 1998: '法国', 2002: '巴西', 2006: '意大利', 2010: '西班牙', 2014: '德国', 2018: '法国'}

使用 fromkeys(a,0) 的方式可以直接创建出 { 1994:0, 1998:0, …… , 2018:0 }的字典,假设赋值给变量 d2 。接下来如果想把0替换为列表 b 中对应位置的元素,就需要写一个循环。

该循环的思路可以从键和值的对应关系中发现:1994 对应 b[0] ,1998对应 b[1] ,2002对应 b[2] …… 。仔细观察其中的规律,就是 年份 k 对应 b 中下标为 (k-1994)//4 的元素。据此思路,就可以扫描字典 d2 中的每一个键(即年份数字),然后将 b[ (k-1994)//4 ] 作为其值。

这里需要注意的是:由于 (k-1994)/4 得到的是浮点数比如 2.0 ,而浮点数不能作为列表中的索引值,所以需要使用整数除法符号 // 。

练习2:字典实现日志分析

首先下载 某企业ERP系统登录日志 文件。该日志是一个UTF-8编码的文本文件,里面每行内容都是一次用户登录记录,包括 日期、时间、姓名、停留时长四个部分,均由空格隔开。

请编写一个程序,统计出该日志文件中,每个员工的总访问次数,然后使用 print 将结果打印在屏幕上。完成之后,进一步修改程序,将结果保存到一个新建的 Excel 文件中,A列保存姓名、B列保存访问次数。

  1. with open('python02_15_01.txt', 'r', encoding = 'utf8') as f:
  2. data = f.readlines()
  3. data = [s.split(' ') for s in data]
  4. data = [[x[0], x[1], x[2], int(x[3].strip())] for x in data]
  5. data[:10]
  1. [['2018/5/3', '6:00', '王二', 579],
  2. ['2018/5/4', '0:09', '王五', 874],
  3. ['2018/5/4', '11:48', '赵六', 279],
  4. ['2018/5/5', '3:54', '田七', 953],
  5. ['2018/5/5', '6:35', '崔九', 982],
  6. ['2018/5/5', '20:44', '黄大', 73],
  7. ['2018/5/6', '20:32', '王二', 895],
  8. ['2018/5/7', '3:07', '崔九', 972],
  9. ['2018/5/7', '5:35', '刘八', 87],
  10. ['2018/5/7', '23:08', '张三', 146]]
  1. employees = [x[2] for x in data]
  2. visits = {x[2]:employees.count(x[2]) for x in data}
  3. print(visits)
  1. {'王二': 92, '王五': 103, '赵六': 101, '田七': 92, '崔九': 93, '黄大': 97, '刘八': 105, '张三': 125, '李四': 93, '顾十': 99}
  1. import xlwt
  2. workbook = xlwt.Workbook(encoding = 'utf8')
  3. worksheet = workbook.add_sheet('sheet1')
  4. a = [[x, visits[x]] for x in visits]
  5. for i in range(len(a)):
  6. worksheet.write(i, 1, a[i][0])
  7. worksheet.write(i, 2, a[i][1])
  8. workbook.save('visits.xls')
  1. # 参考:
  2. with open('python02_15_01.txt',encoding='utf-8') as f:
  3. all_lines = f.readlines()
  4. all_names = [ i.split(' ')[2] for i in all_lines]
  5. all_counts = { k : all_names.count(k) for k in all_names}
  6. for i in all_counts:
  7. print(i, all_counts[i])
  1. 王二 92
  2. 王五 103
  3. 赵六 101
  4. 田七 92
  5. 崔九 93
  6. 黄大 97
  7. 刘八 105
  8. 张三 125
  9. 李四 93
  10. 顾十 99