【python 让繁琐工作自动化】第5章 字典和结构化数据


Automate the Boring Stuff with Python: Practical Programming for Total Beginners (2nd Edition)
Written by Al Sweigart.
The second edition is available on 2019.10.29


5.1 字典数据类型

与列表一样,字典也是许多值的集合。但是与列表索引不同,字典索引可以使用许多不同的数据类型,而不仅仅是整数。字典的索引称为(keys),键及其关联的值称为键-值对(key-value pair)。
在代码中,字典输入时带花括号 {}。

>>> myCat = {'size': 'fat', 'color': 'gray', 'disposition': 'loud'}
>>> 'My cat has ' + myCat['color'] + ' fur.'
'My cat has gray fur.'
>>> spam = {12345: 'Luggage Combination', 42: 'The Answer'}
>>> spam[12345])
'Luggage Combination'

字典 myCat 的键是 ‘size’、‘color’ 和 ‘disposition’。这些键相应的值是 ‘fat’、‘gray’ 和 ‘loud’。可以通过它们的键访问这些值。

字典和列表

与列表不同,字典中的项是不排序的。一个名为 spam 的列表中的第一项是 spam[0]。但是字典里没有“第一个”表项。虽然表项的顺序对于确定两个列表是否相同很重要,但是在字典中输入键-值对的顺序并不重要。

>>> spam = ['cats', 'dogs', 'moose']
>>> bacon = ['dogs', 'moose', 'cats']
>>> spam == bacon
False
>>> eggs = {'name': 'Zophie', 'species': 'cat', 'age': '8'}
>>> ham = {'species': 'cat', 'age': '8', 'name': 'Zophie'}
>>> eggs == ham
True

因为字典是不排序的,所以不能像列表那样切片。

尝试访问字典中不存在的键,将导致 KeyError 出错信息。

spam = {'name': 'Zophie', 'age': 7}
c = spam['color'] # 报错:KeyError: 'color'

例,利用字典保存生日数据:

birthdays = {'Alice': 'Apr 1', 'Bob': 'Dec 12', 'Carol': 'Mar 4'}

while True:
	print('Enter a name: (blank to quit)')
	name = input()
	if name == '':
		break

	if name in birthdays:
		print(birthdays[name] + ' is the birthday of ' + name)
	else:
		print('I do not have birthday information for ' + name)
		print('What is their birthday?')
		bday = input()
		birthdays[name] = bday
		print('Birthday database updated.')
keys()、values() 和 items() 方法

keys()、values() 和 items() 三种字典方法将返回类似列表的值,分别对应于字典的键、值和键-值对。这些方法返回的值不是真正的列表:它们不能被修改,也没有 append() 方法。但这些数据类型 (分别为 dict_keys、dict_values 和 dict_items) 可以在 for 循环中使用。

spam = {'color': 'red', 'age': 42}
print(spam.values()) # 打印 dict_values(['red', 42])
for v in spam.values():
	print(v)
	
print(spam.keys()) # 打印 dict_keys(['color', 'age'])
for k in spam.keys():
	print(k)
	
print(spam.items()) # 打印 dict_items([('color', 'red'), ('age', 42)])
for i in spam.items():
	print(i)

# 使用 list() 函数可以从这些方法中得到真正的列表
print(list(spam.keys())) # 打印 ['color', 'age']

# 使用多重赋值的技巧
for k, v in spam.items():
	print('Key: ' + k + ' Value: ' + str(v))
检查键或值是否存在于字典中

可以使用 in 和 not in 操作符来查看某个键或值是否存在于字典中。

>>> spam = {'name': 'Zophie', 'age': 7}
>>> 'name' in spam.keys()
True
>>> 'Zophie' in spam.values()
True
>>> 'color' in spam.keys()
False
>>> 'color' not in spam.keys()
True
>>> 'color' in spam
False

如果想要检查一个值是否为字典中的键,可以用关键字 in(或 not in),作用于该字典本身。

get() 方法

字典有一个 get() 方法,它有两个参数:要检索的值的键,以及当键不存在时,返回的备用值。

picnicItems = {'apples': 5, 'cups': 2}
print('I am bringing ' + str(picnicItems.get('cups', 0)) + ' cups.') # 打印 'I am bringing 2 cups.'
print('I am bringing ' + str(picnicItems.get('eggs', 0)) + ' eggs.') # 打印 'I am bringing 0 eggs.'

如果没有使用get(),当键不存在时,代码会导致错误消息。

print('I am bringing ' + str(picnicItems['eggs']) + ' eggs.') # 报错:KeyError: 'eggs'
setdefault() 方法

常常需要为字典中某个键设置一个默认值,仅当该键没有任何值时使用它。代码看起来像这样:

spam = {'name': 'Pooka', 'age': 5}
if 'color' not in spam:
    spam['color'] = 'black'

setdefault() 方法在一行代码中实现上面的代码。传递给方法的第一个参数是要检查的键,第二个参数是要在该键不存在时设置的值。如果键确实存在,setdefault() 方法将返回键的值。

>>> spam = {'name': 'Pooka', 'age': 5}
>>> spam.setdefault('color', 'black')
'black'
>>> spam
{'color': 'black', 'age': 5, 'name': 'Pooka'}
>>> spam.setdefault('color', 'white')
'black'
>>> spam
{'color': 'black', 'age': 5, 'name': 'Pooka'}

计算一个字符串中每个字符出现的次数:

message = 'It was a bright cold day in April, and the clocks were striking thirteen.'
count = {}

for character in message:
    count.setdefault(character, 0)
    count[character] = count[character] + 1

print(count)

5.2 美观打印

如果在程序中导入 pprint 模块,就可以访问 pprint() 和 pformat() 函数,它们可以“美观打印”字典的值。如果希望字典中条目的显示比 print() 提供的更清晰时,这是很有帮助的。

import pprint
message = 'It was a bright cold day in April, and the clocks were striking thirteen.'
count = {}

for character in message:
    count.setdefault(character, 0)
    count[character] = count[character] + 1

pprint.pprint(count)

上面的代码运行输出看起来更干净,键也排序了。其结果如下:

{' ': 13,
 ',': 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}

pprint() 函数在字典本身包含嵌套列表或字典时特别有用。
如果希望将美化的文本作为字符串值而不是在屏幕上显示,可以调用 pprint.pformat()。下面两行代码等价:

pprint.pprint(someDictionaryValue)
print(pprint.pformat(someDictionaryValue))

5.3 使用数据结构对真实世界的事物建模

可以用列表和字典对现实世界的事物建模,比如棋盘。对于第一个示例,将使用一个比国际象棋更简单的游戏:井字棋(tic-tac-toe)。

井字棋盘

井字棋盘看起来像一个大的井字符号 (#),有 9 个位置,每个位置可以包含 X、O 或空。
要用字典表示棋盘,可以为每个位置分配一个字符串值的键。
键 ‘top-R’ 的字符串值可以表示右上角,键 ‘low-L’ 的字符串值可以表示左下角,键 ‘mid-M’ 的字符串值可以表示中间,依此类推。
这个字典就是表示井字棋盘的数据结构。将这个字典表示的棋盘保存在名为 theBoard 的变量中。
创建一个函数,将棋盘字典打印到屏幕上。

# ticTacToe.py

theBoard = {'top-L': ' ', 'top-M': ' ', 'top-R': ' ',
            'mid-L': ' ', 'mid-M': ' ', 'mid-R': ' ',
            'low-L': ' ', 'low-M': ' ', 'low-R': ' '}

# 打印井字棋盘
def printBoard(board):
    print(board['top-L'] + '|' + board['top-M'] + '|' + board['top-R'])
    print('-+-+-')
    print(board['mid-L'] + '|' + board['mid-M'] + '|' + board['mid-R'])
    print('-+-+-')
    print(board['low-L'] + '|' + board['low-M'] + '|' + board['low-R'])

# 允许玩家输入他们的动作
turn = 'X'
for i in range(9):
	printBoard(theBoard)
	print('Turn for ' + turn + '. Move on which space?')
	move = input()
	theBoard[move] = turn
	if turn == 'X':
		turn = 'O'
	else:
		turn = 'X'

这并不是一个完整的井字棋游戏,例如,它不会检查一个玩家是否获胜,但它足以查看数据结构如何在程序中使用。
完整的井字棋游戏程序的源代码可以在 http://nostarch.com/automatestuff/ 的参考资料中找到。

嵌套字典和列表

例,使用包含其他字典的字典,来记录谁带了什么去野餐。totalBrought() 函数可以读取这个数据结构,计算所有客人带来的食物的总数。

allGuests = {'Alice': {'apples': 5, 'pretzels': 12},
             'Bob': {'ham sandwiches': 3, 'apples': 2},
             'Carol': {'cups': 3, 'apple pies': 1}}

def totalBrought(guests, item):
	numBrought = 0
	for k, v in guests.items():
		numBrought = numBrought + v.get(item, 0)
	return numBrought

print('Number of things being brought:')
print(' - Apples         ' + str(totalBrought(allGuests, 'apples')))
print(' - Cups           ' + str(totalBrought(allGuests, 'cups')))
print(' - Cakes          ' + str(totalBrought(allGuests, 'cakes')))
print(' - Ham Sandwiches ' + str(totalBrought(allGuests, 'ham sandwiches')))
print(' - Apple Pies     ' + str(totalBrought(allGuests, 'apple pies')))

5.4 实践项目

奇幻游戏的库存

正在创建一款奇幻电子游戏。对玩家库存建模的数据结构将是一个字典,其中键是描述库存中物品的字符串值,值是一个整数值,说明玩家拥有该物品的数量。例如,字典值 {‘rope’: 1, ‘torch’: 6, ‘gold coin’: 42, ‘dagger’: 1, ‘arrow’: 12} 表示玩家有 1 根绳子,6 支火炬,42 枚金币,等等。
编写一个名为 displayInventory() 的函数,该函数将接受任何可能的“库存”并如下所示:
Inventory:
12 arrow
42 gold coin
1 rope
6 torch
1 dagger
Total number of items: 62
提示:可以使用for循环遍历字典中的所有键。

# inventory.py
stuff = {'rope': 1, 'torch': 6, 'gold coin': 42, 'dagger': 1, 'arrow': 12}

def displayInventory(inventory):
    print("Inventory:")
    item_total = 0
    for k, v in inventory.items():
        # FILL IN THE CODE HERE
    print("Total number of items: " + str(item_total))

displayInventory(stuff)

解:文件 inventory.py 的代码如下:

# inventory.py
stuff = {'rope': 1, 'torch': 6, 'gold coin': 42, 'dagger': 1, 'arrow': 12}

def displayInventory(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))

displayInventory(stuff)
列表到字典的函数,用于奇幻游戏的库存

想象一下,一条被击败的龙的战利品表示为这样的字符串列表:
dragonLoot = [‘gold coin’, ‘dagger’, ‘gold coin’, ‘gold coin’, ‘ruby’]
编写一个名为 addToInventory(inventory, addedItems) 的函数,其中 inventory 参数是一个代表玩家库存的字典(如前一个项目中所示),而 addedItems 参数是一个类似 dragonLoot 的列表。addToInventory() 函数应该返回一个表示已更新库存的字典。注意,addedItems 列表可以包含多个同样的项。代码可能是这样的:

def addToInventory(inventory, addedItems):
    # your code goes here

inv = {'gold coin': 42, 'rope': 1}
dragonLoot = ['gold coin', 'dagger', 'gold coin', 'gold coin', 'ruby']
inv = addToInventory(inv, dragonLoot)
displayInventory(inv)

前一个程序(使用前一个项目中的 displayInventory() 函数)将输出以下内容:
Inventory:
45 gold coin
1 rope
1 ruby
1 dagger
Total number of items: 48

def addToInventory(inventory, addedItems):
    for item in addedItems:
    	count = inventory.setdefault(item, 0)
    	inventory[item] = count + 1
    return inventory

inv = {'gold coin': 42, 'rope': 1}
dragonLoot = ['gold coin', 'dagger', 'gold coin', 'gold coin', 'ruby']
inv = addToInventory(inv, dragonLoot)
displayInventory(inv)

学习网站:
https://automatetheboringstuff.com/chapter5/

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值