字典
是另一种可变容器模型,且可存储任意类型对象
键一般是唯一的,如果重复,最后的一个键值对会替换前面的,值不需要唯一
d = {key1 : value1, key2 : value2 }
d = {'Z' : '字', 'D' : '典' }
字典创建与删除
1). 简单字典创建
# 代码
ddict = {'name':'kobe','age':'32','id':'24'}
print(ddict['name'])
print(ddict['age'])
print(ddict['id'])
#运行结果
kobe
32
24
2). 内建方法:fromkeys
字典中的key有相同的value值,默认为None
# 代码
ddict = {}.fromkeys(('name', 'age', 'id'), 'kobe')
print(ddict)
ddict = {}.fromkeys(('name', 'age', 'id'))
print(ddict)
# 运行结果
{'name': 'kobe', 'age': 'kobe', 'id': 'kobe'}
{'name': None, 'age': None, 'id': None}
3). zip间接创建
# 代码
ddict = zip(['name', 'age', 'id'], ['kobe', '34', '24'])
print(dict(ddict))
# 运行结果
{'name': 'kobe', 'age': '34', 'id': '24'}
字典内建方法
字典应用案例1(词频统计)
作为字典(key-value)的经典应用题目,单词统计几乎出现在每一种语言键值对学习后的必练题目,主要需求: 写一个函数wordcount统计一篇文章的每个单词出现的次数(词频统计)。统计完成后,对该统计按单词频次进行排序
# 代码
content = """
hello python
hello westos
hello world
"""
# 1). 将每个单词拿出来(split) ---> ['hello', 'python', .......]
content_list = content.split()
# 2). 定义一个空字典d={}, 用来存储每个单词出现的次数;
counterDict = {}
# 3). 依次遍历列表的每个单词,
# d['hello'] = 2
# d['pyhton'] = 1
for item in content_list:
# 如果该单词在字典中没有统计过, 赋值为1;
if item not in counterDict:
counterDict[item] = 1
# 如果该单词在字典中已经统计过, 在原有的个数基础上加1;
else:
counterDict[item] += 1
print(counterDict)
# 运行结果
{'hello': 3, 'python': 1, 'westos': 1, 'world': 1}
字典应用案例2(列表去重)
通过字典的方式去重,因为字典的key值是不能重复的
# 代码
li = [1, 2, 3, 4, 65, 1, 2, 3]
print({}.fromkeys(li).keys())
# 运行结果
dict_keys([1, 2, 3, 4, 65])
字典应用案例3(switch语句实现)
注意: python中没有switch语句, 如何间接实现?
if实现switch语句
# 代码
grade = input("请输入等级:")
if grade == 'A':
print("优秀")
elif grade == 'B':
print("良好")
elif grade == 'C':
print("及格")
else:
print("无效的成绩")
# 运行结果
请输入等级:B
良好
请输入等级:F
无效的成绩
通过字典实现switch语句
# 代码
gradeDic = {'A': "优秀", 'B': "良好", 'C': "及格"}
grade = input("请输入等级:") # A B
# 方法一: 根据key值获取value值: gradeDic[grade]
# 如果key值存在, 返回value值;
# 如果key值不存在, 报错;
if grade in gradeDic:
print(gradeDic[grade])
else:
print("无效的成绩")
# 方法二: 根据key值获取value值: dic.get(key)
# 如果key值存在, 返回value值;
# 如果key值不存在,返回None或者你指定的默认值;
print(gradeDic.get(grade, "无效的成绩")) #默认值是"无效的成绩"
# 运行结果
请输入等级:A
优秀
优秀
请输入等级:S
无效的成绩
无效的成绩
字典应用案例4(基于用户协同过滤算法的电影推荐代码demo)
题目需求: 假设已有若干用户名字及其喜欢的电影清单,现有某用户,已看过并喜欢一些电影,现在想找个新电影看看,又不知道看什么好。根据已有数据,查找与该用户爱好最相似的用户,也就是看过并喜欢的电影与该用户最接近,然后从那个用户喜欢的电影中选取一个当前用户还没看过的电影,进行推荐。
{ "小明": {"绿皮书", "流浪地球"}, "小红": {"绿皮书", "流浪地球"}, }
技能要点: Python内置函数以及内置字典与集合的用法
推荐资料: http://www.voidcn.com/article/p-yayskzin-zt.html
第一步:产生数据
# 代码
"""
假设已有若干用户名字及其喜欢的电影清单,现有某用户,已看过并喜欢一些电影,现在想找个新电影看看,又不知道看什么好。
根据已有数据,查找与该用户爱好最相似的用户,也就是看过并喜欢的电影与该用户最接近,然后从那个用户喜欢的电影中选取
一个当前用户还没看过的电影,进行推荐。
"""
import random
import pprint
# 1). 设计沪剧存储的数据结构; 字典里面, value值是列表;
movieDb = {}
# 2). 随机生成100个电影名
movies = []
for item in range(100):
movie_name = '电影' + str(item)
movies.append(movie_name)
print(movies)
# 3). 随机生成用户和喜欢电影的集合;
for item in range(100):
name = 'user' + str(item)
# random.sample([1, 2, 3, 4], 3) ---> 从指定的序列中拿出指定个数据出来;返回的是列表;
user_movies = random.sample(movies, random.randint(2, 30))
movieDb[name] = user_movies
# 4). 更加友好的打印数据信息;
# pprint.pprint(movieDb)
# 5). 如何写入文件中;
# json: javascript object
import json
# 以写的权限打开文件;
f = open('movie.txt', 'w')
# 将python的字典转成json格式的字符串, 并保存到文件对象f中;
# ensure_ascii=False: 中文显示正确
# indent=4缩进进为4个空格;
json.dump(movieDb, f, ensure_ascii=False, indent=4)
# 关闭文件;
f.close()
数据结果(保存在文件中,由于数据量太大,这里只截取了一部分)
{
"user0": [
"电影44",
"电影12",
"电影20",
"电影86",
"电影43",
"电影8",
"电影23",
"电影69",
"电影30",
"电影83",
"电影36",
"电影16",
"电影17",
"电影3",
"电影79",
"电影24",
"电影91",
"电影74",
"电影14",
"电影9",
"电影18",
"电影21",
"电影70",
"电影84",
"电影88"
],
"user1": [
"电影72",
"电影28",
"电影13",
"电影90",
"电影88",
"电影64",
"电影33",
"电影91",
"电影30"
],
"user2": [
"电影5",
"电影56",
"电影81",
"电影92",
"电影65",
"电影71",
"电影72",
"电影9",
"电影66"
],
"user3": [
"电影47",
"电影27",
"电影78",
"电影25",
"电影99",
"电影34",
"电影94",
"电影6",
"电影56",
"电影81",
"电影59",
"电影89",
"电影86",
"电影30",
"电影52",
"电影93",
"电影92",
"电影0",
"电影66",
"电影44",
"电影64",
"电影13",
"电影76"
]
第二步:推荐电影
# 代码
"""
JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式,易于人阅读和编写。
json模块是实现Python 对象和JSON 字符串之间转换的模块;
"""
import json
"""
pprint 包含一个“美观打印机”,用于生成数据结构的一个美观视图。格式化工具会生成数据结构的一些表示,不仅可以由解释器正确地解析,
而且便于人类阅读。输出尽可能放在一行上,分解为多行时则需要缩进。
"""
import pprint
# Counter类的目的是用来跟踪值出现的次数。它是一个无序的容器类型,以字典的键值对形式存储,其中元素作为key,其计数作为value。计数值可以是任意的Interger(包括0和负数)。
from collections import Counter
# 1). 从文件中读取用户和电影数据
# 打开文件信息, 默认打开方式是读
f = open('movie.txt')
# 将文件中的json字符串转成python便于处理的字典数据类型;
movieDb = json.load(f)
# 关闭文件
f.close()
# print(type(movieDb))
# pprint.pprint(movieDb)
searchUser = input("请输入需要推荐的用户名: ")
# 2). 依次遍历字典的每一个元素, 查找跟user3用户交集最多的用户(user1);
# searchUser 和自己是不需要比较的;
searchUserMovies = set(movieDb.pop(searchUser))
# result存储 searchUser和每个用户喜欢电影交集的个数;= {'user1': 1, 'user2': 2}
result = {}
for user, movies in movieDb.items():
# 求用户和searchUser交集的个数;
interaction_count = len(set(movies) & searchUserMovies)
# 并存储到字典中;
result[user] = interaction_count
# 3). 打印统计结果
# pprint.pprint(result)
# 4). 对于结果进行排序
# 对字典的value值进行排序, 由大到小;
counter = Counter(result)
# 打印排序结果的前几个, 目前指定的是前5个;
top5UserCounter = counter.most_common(5)
# print(top5UserCounter)
# 5). 获取前5个跟searchUser相似用户喜欢的电影, 并求并集;
# 获取所有跟searchUser最相关的前5个用户名;
top5User = dict(top5UserCounter).keys()
# allUnionMovies 跟searchUser最相关的前5个用户喜欢的电影的并集; 定义空集合用set()
allUnionMovies = set()
for user in top5User:
# 获取用户喜欢的电影; 列表;
# 集合添加元素: add: 添加一个元素; update: 一次添加多个元素;
allUnionMovies.update(movieDb[user])
# 前5个用户喜欢的电影的并集 - searchUser喜欢的电影
allRecommendMovies = allUnionMovies - searchUserMovies
print(allRecommendMovies)
allRecommendMoviesList = list(allRecommendMovies) # 集和不能用json,因此转换成了列表
m = open('RecommendMovies.txt', 'w') # 由于在运行窗口显示不好查看,因此写入一个文件中查看
json.dump(allRecommendMoviesList, m, ensure_ascii=False, indent=4)
f.close()
推荐电影的数据:
[
"电影45",
"电影63",
"电影30",
"电影60",
"电影54",
"电影89",
"电影57",
"电影77",
"电影19",
"电影3",
"电影22",
"电影26",
"电影68",
"电影91",
"电影0",
"电影92",
"电影73",
"电影88",
"电影53",
"电影78",
"电影29",
"电影13",
"电影36",
"电影6",
"电影62",
"电影44",
"电影71",
"电影95",
"电影8",
"电影51",
"电影1",
"电影18",
"电影97",
"电影11",
"电影49",
"电影42",
"电影2",
"电影38",
"电影55",
"电影46",
"电影23",
"电影9",
"电影98",
"电影84",
"电影85",
"电影81",
"电影27",
"电影28",
"电影69"
]
一键多值字典:defaultdict
collections.defaultdict类,本身提供了默认值的功能, 默认值可以是整形,列表,集合等.
需求:我们想要一个能将键(key)映射到多个值的字(即所谓的一键多值字典)
解决方案: 1). 字典是一种关联容器,每个键都映射到一个单独的值上。如果想让键映射到多个值,需要将这些多个值保存到容器(列表或者集合)中。
2). 利用collections模块中的defaultdict类自动初始化第一个值,这样只需关注添加元素.
# 代码
from collections import defaultdict
# 创建一个空字典, 所有的value值默认是空列表[]
dict1 = defaultdict(list)
# 判断'a'这个key值是否存在, 如果不存在, 返回[], .append, 在空列表后面追加元素1;
dict1['a'].append(1)
# 判断'a'这个key值是否存在, 如果存在, 获取value值, .append, 在原有的value值后面追加元素1;
dict1['a'].append(2)
dict1['b'].append(4)
print(dict1)
# 创建一个空字典, 所有的value值默认是空集合set()
dict1 = defaultdict(set)
# 判断'a'这个key值是否存在, 如果不存在, 返回set(), .add, 在空列表后面追加元素1;
dict1['a'].add(1)
# 判断'a'这个key值是否存在, 如果存在, 获取value值, .add, 在原有的value值后面追加元素1;
dict1['a'].add(2)
dict1['b'].add(4)
print(dict1)
# 运行结果
defaultdict(<class 'list'>, {'a': [1, 2], 'b': [4]})
defaultdict(<class 'set'>, {'a': {1, 2}, 'b': {4}})
字典应用案例5(一键多值字典:defaultdict)
用defaultDict来做一个练习,把list(随机生成50个1-100之间的随机数)中大于66的元素和小于66的元素存成
{
'大于66的元素': [71,8 2, ,83],
'小于66的元素': [1, 2, 3]
}
# 代码
import random
from collections import defaultdict
import pprint
# 1). 随机生成50个1-100之间的随机数
count = 50
allNums = []
for item in range(count):
allNums.append(random.randint(1, 100))
# 2). 大于66的元素和小于66的元素
classifyNums = defaultdict(list)
for num in allNums:
if num > 66:
classifyNums['大于66的元素'].append(num)
elif num < 66:
classifyNums['小于66的元素'].append(num)
pprint.pprint(classifyNums)
# 运行结果
{'大于66的元素': [96, 84, 81, 68, 82, 87, 90, 98, 84, 67, 67, 84, 85],
'小于66的元素': [31,
40,
5,
43,
31,
21,
61,
31,
35,
33,
26,
64,
45,
39,
40,
4,
17,
65,
39,
37,
51,
50,
13,
44,
50,
10,
13,
57,
43,
4,
9,
35,
42,
35,
41,
14]})
内置数据结构总结
1)可变数据类型:可以增删改。可变数据类型,允许变量的值发生变化,即如果对变量进行append、+=等这种操作后,只是改变了变量的值,而不会新建一个对象,变量引用的对象的地址也不会变化,不过对于相同的值的不同对象,在内存中则会存在不同的对象,即每个对象都有自己的地址,相当于内存中对于同值的对象保存了多份,这里不存在引用计数,是实实在在的对象。
不可变数据类型:不可以增删改。python中的不可变数据类型,不允许变量的值发生变化,如果改变了变量的值,相当于是新建了一个对象,而对于相同的值的对象,在内存中则只有一个对象,内部会有一个引用计数来记录有多少个变量引用这个对象。
2)序列: Python包含列表、元组、字符串、集合, 字典等内建的序列。所有序列类型都可以进行某些特定的操作。可以for循环 有序序列: 这些操作包括:索引(indexing)、切片(sliceing)、连接操作符(adding)、重复操作符(multiplying)以及成员操作符。
非序列:int, long, float, bool, complex
3)可以for循环: 字符串, 列表, 元组, 集合, 字典
不可以for循环:数值类型(int, long, float, bool, complex)
练习1
问题描述: 有一个列表,其中包括 10 个元素,例如这个列表是[1,2,3,4,5,6,7,8,9,0],要求将列表中的每个元素一次向前移动一个位置,第一个元素到列表的最后,然后输出这个列表。最终样式是[2,3,4,5,6,7,8,9,0,1]
# 代码
list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
list0 = list.pop(0)
list.append(list0)
print(list)
# 运行结果
[2, 3, 4, 5, 6, 7, 8, 9, 0, 1]
练习2
问题描述:按照下面的要求实现对列表的操作:
1). 产生一个列表,其中有 40 个元素,每个元素是 50 到 100 的一个随机整数
2). 如果这个列表中的数据代表着某个班级 40 人的分数,请计算成绩低于平均分的学生人数
3). 对上面的列表元素从大到小排序并输出li.sort(reverse=True)
# 代码
import random
scoreList = []
scoreCount = 0 # 计算总分数
studentCount = 0 # 统计分数低于平均分的学生
for item in range(40): # 产生40个学生的成绩
score = random.randint(50, 100)
scoreList.append(score)
scoreCount += score
print(scoreList)
length = len(scoreList) # 学生人数
average = scoreCount / length # 平均分
print(average)
for item in scoreList:
if item < average:
studentCount += 1
print(studentCount)
scoreList.sort(reverse=True) # 从大到小排序
print(scoreList)
# 运行结果
[98, 100, 58, 55, 78, 80, 66, 63, 89, 95, 89, 71, 68, 64, 82, 86, 98, 91, 72, 66, 93, 56, 72, 77, 84, 87, 94, 79, 50, 65, 90, 64, 60, 92, 65, 73, 56, 89, 91, 92]
77.45
19
[100, 98, 98, 95, 94, 93, 92, 92, 91, 91, 90, 89, 89, 89, 87, 86, 84, 82, 80, 79, 78, 77, 73, 72, 72, 71, 68, 66, 66, 65, 65, 64, 64, 63, 60, 58, 56, 56, 55, 50]
练习3
问题描述: 如果将一句话作为一个字符串,那么这个字符串中必然会有空格(这里仅讨论英文),比如"How are you.",但有的时候,会在两个单词之间多大一个空格。现在的任务是,如果一个字符串中有连续的两个空格,请把它删除。
# 代码
str1 = "how are you "
print(str1.split())
print(' '.join(str1.split()))
# 运行结果
['how', 'are', 'you']
how are you