1.字典数据结构
在Python中,字典(Dictionary)是一种内置的数据类型,用于存储键值对(key-value pairs)。字典是无序的、可变的,并且键必须是唯一的。字典的每个键值对用冒号(:)分隔,键值对之间用逗号(,)分隔,整个字典包围在花括号({})中。
# 创建一个空字典
empty_dict = {}
# 创建一个带有初始键值对的字典
student_scores = {
'Alice': 85,
'Bob': 78,
'Charlie': 92
}
2.字典和列表
字典和列表是Python中两种常用的数据结构,它们之间有一些重要的区别和相似之处。
- 存储方式:列表是顺序存储的,即按照元素的插入顺序存储。字典则是无序存储的,即元素的存储顺序可能与插入顺序不同。
- 访问方式:列表通过索引访问元素,而字典通过键访问值。例如,可以通过
list[index]
访问列表中的元素,通过dict[key]
访问字典中的值。 - 元素类型:列表可以存储任意类型的元素,包括数字、字符串、列表、字典等。字典的键必须是不可变类型,如字符串、数字或元组,而值可以是任意类型。
- 长度:列表的长度是固定的,而字典的长度是可变的。可以通过
len(list)
获取列表的长度,通过len(dict)
获取字典的长度。 - 操作:列表支持多种操作,如添加、删除、修改元素等。字典也支持多种操作,如添加、删除、修改键值对等。
- 尽管字典是不排序的,但是字典可以用任意值作为键,而列表只能用整数作为索引。
python中字典的排序
# 示例字典
my_dict = {'b': 2, 'a': 1, 'c': 3}
# 按键排序
sorted_dict = {k: my_dict[k] for k in sorted(my_dict)}
print(sorted_dict)
输出:
{'a': 1, 'b': 2, 'c': 3}
在这个例子中,我们首先使用sorted()
函数对字典的键进行排序,然后使用字典推导式创建一个新的字典,其中键按照排序后的顺序排列。最后,我们打印出排序后的字典。
如果不需要一个新的字典,而是需要一个排序后的键值对列表:
# 示例字典
my_dict = {'b': 2, 'a': 1, 'c': 3}
sorted_dict = sorted(my_dict.items())
print(sorted_dict)
输出:
[('a', 1), ('b', 2), ('c', 3)]
在这个例子中,我们使用items()
方法将字典中的键值对转换为元组列表,然后使用sorted()
函数对列表进行排序。最后,我们打印出排序后的键值对列表。
key() value() items()方法
有3个字典方法可以返回类似列表的值,它们是keys()、values()和items()。这些方法返回的值不是列表,它们是字典视图对象。字典视图对象支持迭代,但不支持连接和切片等序列操作。
可用于for循环
spam ={'color':'red','age':42}
for v in spam.values():
print(v)
for k in spam.keys():
print(k)
for i in spam.items():
print(i)
red
42
color
age
('color', 'red')
('age', 42)
注意 inems()方法返回的是元组,每个元组包含一个键值对
3.字典推导式
字典推导式是一种简洁的创建字典的方式。它使用类似于列表推导式的语法,但用于创建字典。字典推导式的基本语法如下:
{key_expression: value_expression for item in iterable}
其中,key_expression和value_expression是用于生成键和值的表达式,iterable是一个可迭代对象,用于生成字典的元素。例如,以下代码使用字典推导式创建了一个将字符串中的每个字符映射到其出现次数的字典:
message = "hello world"
count = {char: message.count(char) for char in message}
print(count)
输出:
{'h': 1, 'e': 1, 'l': 3, 'o': 2, ' ': 1, 'w': 1, 'r': 1, 'd': 1}
在这个例子中,字典推导式遍历了字符串message中的每个字符,并使用count()方法计算了每个字符在字符串中出现的次数。然后,将字符作为键,出现次数作为值,创建了一个新的字典count。
推导式是一个很不错的写法 值得学习
4.检查字典中是否存在键或值
in not in 可以检查值是否存在列表中,但是字典中只能检查键是否存在
spam = {'color':'red','age':42}
print('color' in spam)
print('age' in spam)
print('name' in spam)
print('name' not in spam)
True
True
False
True
其中 'color' in spam 本身是一种简写 实际上相当于 'color' in spam.key()
5.字典的get()方法
在访问一个 键的值时,如果键不存在,就会引发KeyError异常。为了避免这种情况,可以使用字典的get()方法。get()方法接受两个参数:要查找的键和默认值。如果键存在,get()方法将返回键对应的值。如果键不存在,get()方法将返回默认值。例如:
picnicitems = {'apples': 5, 'cups': 2}
print('I am bringing ' +str(picnicitems.get('cups',4)) + ' cups.')
print('I am bringing ' +str(picnicitems.get('eggs',4)) + ' cups.')
输出结果
- I am bringing 2 cups.
- am bringing 4 cups.
因为 picnicItems 字典中没有'egg'键,get()方法返回的默认值是 4。不使用 get(),代码就会产生一个错误消息,现在3.11版本中
不会直接报错 会输出:I am bringing None cups.
字典的get()方法还可以接受一个可选的第三个参数,用于指定一个函数,该函数将在键不存在时被调用。例如:
def hello():
print('Hello, world!')
spam = {'color': 'red', 'age': 42}
spam.get('name', hello())
输出结果:
- Hello, world!
- None
这个就很优秀了,哈哈
6.字典的setdefault()方法
字典的setdefault()方法与get()方法类似,但setdefault()方法在键不存在时,会设置一个默认值。例如:
spam = {'color': 'red', 'age': 42}
spam.setdefault('color', 'blue')
spam.setdefault('name', 'Pooka')
print(spam)
输出结果:
区别
- 修改字典:setdefault() 方法会在键不存在时修改字典,添加该键和默认值。而 get() 方法不会修改字典。
- 返回值:setdefault() 方法返回的是键对应的值(如果键不存在,则返回默认值并添加该键和默认值)。get() 方法仅返回键对应的值或默认值,不会修改字典。
- 选择使用哪个方法取决于你是否希望在键不存在时修改字典。如果需要修改字典,可以使用 setdefault();如果不需要修改字典,可以使用 get()。
7.漂亮打印
pprint()和pformat()函数可以用来美化打印字典。例如:
import pprint
message = 'It was a bright cold day in April, and the clocks were strikingthirteen.'
count = {}
for character in message:
count.setdefault(character, 0)
count[character] = count[character] + 1
pprint.pprint(count) # 使用 pprint() 函数美化打印字典
输出结果:
{' ': 12,
',': 1,
'.': 1,
'A': 1,
'I': 1,
'a': 4,
'b': 1,
'c': 3,
'd': 3,
'e': 5,
'g': 2,
'h': 3,
'i': 6,
'k': 2,
'l': 3,
'n': 4,
'o': 2,
'p': 1,
'r': 5,
's': 3,
't': 6,
'w': 2,
'y': 1}
pformat() 函数将返回一个字符串,而不是直接打印字典。例如:
import pprint
message = 'It was a bright cold day in April, and the clocks were strikingthirteen.'
count = {}
for character in message:
count.setdefault(character, 0)
count[character] = count[character] + 1
pprint.pformat(count) # 使用 pprint() 函数美化打印字典
输出结果:
"{' ': 12,\n ',': 1,\n '.': 1,\n 'A': 1,\n 'I': 1,\n 'a': 4,\n 'b': 1,\n 'c': 3,\n 'd': 3,\n 'e': 5,\n 'g': 2,\n 'h': 3,\n 'i': 6,\n 'k': 2,\n 'l': 3,\n 'n': 4,\n 'o': 2,\n 'p': 1,\n 'r': 5,\n 's': 3,\n 't': 6,\n 'w': 2,\n 'y': 1}"
事实上
pprint.pprint(count) 和print(pprint.pformat(count))是等价的
8.使用数据结构对真实世界建模
一个例子,我们将使用比国际象棋简单一点的游戏:井字棋盘:
theBoard = {'top-L': ' ', 'top-M': ' ', 'top-R': ' ',
'mid-L': ' ', 'mid-M': 'X', 'mid-R': ' ',
'low-L': ' ', 'low-M': ' ', 'low-R': ' '}
def print_board(board):
print(board['top-L'] + '|' + board['top-M'] + '|' + board['top-R'])
print('-' * 5)
print(board['mid-L'] + '|' + board['mid-M'] + '|' + board['mid-R'])
print('-' * 5)
print(board['low-L'] + '|' + board['low-M'] + '|' + board['low-R'])
print_board(theBoard)
输出结果:
- X| |
- |X| |
- | | |
这个完整程序后面再研究研究
9.嵌套的字典和列表
列表适用于包含一组有序的值,字典适用于包含关联的键与值,例如下面的程序使用字典包含其他字典,用于记录谁为野餐带来了什么食物。totalBrought()函数可以读取这个数据结构,计算所有客人带来的食物总数:
allGuests = {'Alice':{'apples': 5, 'pretzels':12},
'Bob':{'ham sandwiches': 3, 'apples': 2},
'Carol':{'cups': 3, 'apple pies': 1}
}
def totalBrought(guests,item):
numBrougt = 0
for k,v in guests.items():
numBrougt = numBrougt + v.get(item,0)
return numBrougt
print('Number of things being brought: ')
print(' -Apples ' + str(totalBrought(allGuests,'apples')))
print(' -Cups ' + str(totalBrought(allGuests,'cups')))
print(' -Cupcakes ' + str(totalBrought(allGuests,'cupcakes')))
print(' -Ham Sandwiches ' + str(totalBrought(allGuests,'ham sandwiches')))
print(' -Pretzels ' + str(totalBrought(allGuests,'pretzels')))
输出
-Apples 7
-Cups 3
-Cupcakes 0
-Ham Sandwiches 3
-Pretzels 12
小结
- 空字典的代码是怎样的? # {}
- 一个字典包含键'fow'和值 42,看起来是怎样的?#
- 字典和列表的主要区别是什么? # 字典是键值对,列表是值的序列
- 如果 spam 是{'bar': 100},你试图访问 spam['foo'],会发生什么? # 报错 KeyError: 'foo'
- 如果一个字典保存在 spam 中,表达式'cat' in spam 和'cat' in spam.keys()之间的区别是什么?
在 Python 中,'cat' in spam 和 'cat' in spam.keys() 都是用来检查字典 spam 中是否存在键 'cat'。尽管它们在功能上基本相同,但在实现原理和性能上存在一些差异。
表达式 'cat' in spam:
实现原理: 这个表达式利用了字典对象的特殊方法 __contains__ 来检查键 'cat' 是否存在于字典 spam 中。字典在内部使用哈希表来存储键值对,因此这个操作的时间复杂度接近 O(1),即查找速度非常快。
用途: 这是检查字典中是否存在某个键的最常用和推荐的方式,因为它简洁且高效。
注意事项: 这种方法直接利用了字典的内置优化,因此在大多数情况下性能最佳。
表达式 'cat' in spam.keys():
实现原理: 这个表达式首先调用 spam.keys() 方法,该方法返回一个包含字典所有键的视图对象。然后,它在这个视图对象中进行成员检查。虽然视图对象在内部也是基于哈希表实现的,但这一额外的步骤可能会稍微影响性能。
用途: 这种方法在某些情况下可能有用,比如当你需要显式地获取字典的所有键时。然而,如果仅仅是为了检查键是否存在,直接使用 'cat' in spam 更为直接和高效。
注意事项: 由于需要先获取键的视图对象,这种方法在某些情况下可能不如直接使用 in 运算符高效,尤其是在处理大型字典时。
总结来说,虽然 'cat' in spam 和 'cat' in spam.keys() 在功能上等效,但前者通常更高效且更符合 Python 的惯用法。因此,推荐使用 'cat' in spam 来检查字典中是否存在某个键。
- 如果一个字典保存在变量中,表达式'cat' in spam 和'cat' in spam.values()之间的区别是什么?
'cat' in spam 检查 'cat' 是否是字典的键。
'cat' in spam.values() 检查 'cat' 是否是字典的值。
- 下面代码的简洁写法是什么?
if 'color' not in spam:
spam['color'] = 'black'
如下
spam ={}
spam.setdefault('color','black')
print(spam)
- 什么模块和函数可以用于“漂亮打印”字典值? # pprint.pprint()
10.实践项目
好玩游戏的物品清单
你在创建一个好玩的视频游戏。用于对玩家物品清单建模的数据结构是一个字
典。其中键是字符串,描述清单中的物品,值是一个整型值,说明玩家有多少该物
品。例如,字典值{'rope': 1, 'torch': 6, 'gold coin': 42, 'dagger': 1, 'arrow': 12}意味着玩
家有 1 条绳索、6 个火把、42 枚金币等。
# inventory.py
stuff = {'rope': 1, 'torch': 6, 'gold coin': 42, 'dagger': 1, 'arrow': 12}
def display_inventory(inventory):
print("Inventory:")
item_total = 0
for k, v in inventory.items():
print(str(v) + ' ' + k)
item_total += v
print("Total number of items: " + str(item_total))
display_inventory(stuff)
输出
Inventory:
1 rope
6 torch
42 gold coin
1 dagger
12 arrow
Total number of items: 62
```text
编写一个名为 update_inventory() 的函数,该函数接受两个参数:玩家现有的物
品清单(如上例所示)以及另一个字典,其中包含玩家新获得物品,如 {'arrow': 3,
'gold coin': 50}。update_inventory() 函数应该添加新物品到现有清单中,返回一
个表示更新后清单的字典。
def add_to_inventory(inventory, added_items):
for k, v in added_items.items():
print(added_items)
if k in inventory:
inventory[k] += v
else:
inventory[k] = v
print(inventory)
inv = {'gold coin': 42, 'rope': 1,'皮夹克':10}
stuff1 = {'rope': 1, 'torch': 6, 'gold coin': 42, 'dagger': 1, 'arrow': 12}
add_to_inventory(stuff1, inv)
print(stuff)
输出
{'rope': 2, 'torch': 6, 'gold coin': 84, 'dagger': 1, 'arrow': 12}
{'gold coin': 42, 'rope': 1, '皮夹克': 10}