Python基础:第26课——字典(dict)和集合(set)

一种可通过名称来访问其各个值的数据结构。这种数据结构称为映射(mapping)。字典是Python中唯一的内置映射类型,其中的值不按顺序排列,而是存储在键下。键可能是数、字符串或元组。

26.1字典的用途

假设有如下名单:

>>> names = ['Alice', 'Beth', 'Cecil', 'Dee', 'Earl'] 

如果要存储这些人的电话号码,该如何办呢?一种办法是再创建一个列表。假设只存储四位的分机号,这个列表将类似于:

>>> numbers = ['2341', '9102', '3158', '0142', '5551'] 

创建这些列表后,就可像下面这样查找Cecil的电话号码:

>>> numbers[names.index('Cecil')] 
>>> '3158' 

这不太实用。实际上,你希望能够像下面这样做:

>>> phonebook['Cecil'] 
>>> '3158' 

如何达成这个目标呢?把phonebook定义为字典。

26.2 创建和使用字典

字典类似于下面的表示:

phonebook = {'Alice': '2341', 'Beth': '9102', 'Cecil': '3258'} 

字典由键及其相应的值组成,这种键值对称为项(item)。在前面的示例中,键为名字,而值为电话号码。每个键与其值之间都用冒号(:)分隔,项之间用逗号分隔,而整个字典放在花括号内。空字典(没有任何项)用两个花括号表示,类似于下面这样:{}。

把数据放入dict的方法,除了初始化时指定外,还可以通过key放入:

>>> phonebook['Alice'] = '2341'
>>> phonebook['Alice']
'2341'

由于一个key只能对应一个value,所以,多次对一个key放入value,后面的值会把前面的值冲掉:

>>> phonebook['Alice'] = '4321'
>>> d['Jack']
'4321'
>>> phonebook['Alice'] = '4312'
>>> d['Jack']
'4312'

如果key不存在,dict就会报错:

>>> phonebook['Thomas']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'Thomas'

26.2.1 dict函数

>>> persons = [('name','Gumby'), ('age',42)]
>>> d = dict(persons)
>>> d
{
    
    'name': 'Gumby', 'age': 42}
>>> d = dict(name = 'Gumby', age = 42)
>>> d
{
    
    'name': 'Gumby', 'age': 42}

26.2.2 基本的字典操作

字典与列表的一些相同行为

  • len(d) 返回字典d包含的项数
  • d[k] 返回与键k相关联的值
  • d[k] = v 将值v关联到键k。
  • del d[k]删除键为k的项
  • k in d检查字典d是否包含键为k的项。

26.3 字典和列表的不同

请务必注意,dict内部存放的顺序和key放入的顺序是没有关系的。

  • **键的类型:**字典中的键可以是任何不可变的类型
  • **自动添加:**字典中原来没有的键,也可以给它赋值并添加到字典创建新项。列表中没有的元素不可以赋值,除非使用append方法。
>>> x = [] 
>>> x[42] = 'Foobar' 
Traceback (most recent call last): 
  File "<stdin>", line 1, in ? 
IndexError: list assignment index out of range 
>>> x = {} 
>>> x[42] = 'Foobar' 
>>> x 
{42: 'Foobar'} 
  • **成员资格:**d是字典,l是列表,k in d 查找的是键,v in l查找的是值。

和list比较,dict有以下几个特点:

  1. 查找和插入的速度极快,不会随着key的增加而变慢;
  2. 需要占用大量的内存,内存浪费多。

而list相反:

  1. 查找和插入的时间随着元素的增加而增加;
  2. 占用空间小,浪费内存很少。

所以,dict是用空间来换取时间的一种方法。

dict用在需要高速查找的很多地方,在Python代码中几乎无处不在,正确使用dict非常重要,需要牢记的一条就是dict的key必须是不可变对象

这是因为dict根据key来计算value的存储位置,如果每次计算相同的key得出的结果不同,那dict内部就完全混乱了。这个通过key计算位置的算法称为哈希算法(Hash)。

要保证hash的正确性,作为key的对象就不能变。在Python中,字符串、整数等都是不可变的,因此,可以放心地作为key。而list是可变的,就不能作为key:

>>> key = [1, 2, 3]
>>> d[key] = 'a list'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'

26.4 遍历字典

一个Python字典可能包含几个键—值对,也可能包含数百万个键—值对。鉴于字典可能包含大量的数据,Python支持对字典遍历。遍历字典的方式:可遍历字典的所有键—值对、键或值。

26.4.1 遍历所有的键—值对

先来看一个新字典,它用于存储有关网站用户的信息。下面的字典存储一名用户的用户名、名和姓:

user_0 = { 
	'username': 'efermi', 
	'first': 'enrico', 
	'last': 'fermi', 
	} 

如果要显示该字典中的所有信息,该怎么办呢?可以使用一个for循环来遍历这个字典:
user.py

user_0 = { 
    'username': 'efermi', 
    'first': 'enrico', 
    'last': 'fermi', 
    } 
# print(user_0.items())
for key, value in user_0.items():
	print("\nKey: " + key)
	print("Value: " + value)

要编写遍历字典的for循环,可声明两个变量,用于存储键—值对中的键和值。对于这两个变量,可使用任何名称。下面的代码使用了简单的变量名:
for k, v in user_0.items()
for语句的第二部分包含字典名和方法items(),它返回一个键—值对列表。

print(user_0.items())

dict_items([('username', 'efermi'), ('first', 'enrico'), ('last', 'fermi')])

还记得:width, height = (640, 480)这种赋值方式吗?

for循环依次将每个键—值对存储到指定的两个变量中。前面的示例中,使用这两个变量来打印每个键及其相关联的值。第一条print语句中的\n确保在输出每个键—值对前都插入一个空行:

Key: last 
Value: fermi 

Key: first 
Value: enrico 

Key: username 
Value: efermi 

注意,遍历字典时,键—值对的返回顺序也与存储顺序不同。Python不关心键—值对的存储顺序,而只跟踪键和值之间的关联关系。

示例favorite_languages.py中,其中的键都是人名,值都是语言,在循环中使用变量
name和language,而不是key和value,这让人更容易明白循环的作用:

favorite_languages.py

favorite_languages = { 
    'jen': 'python', 
    'sarah': 'c', 
    'edward': 'ruby', 
    'phil': 'python', 
    } 
for name, language in favorite_languages.items():
    print(name.title() + "'s favorite language is " + language.title() + ".") 

仅使用几行代码,我们就将全部调查结果显示出来了:

Jen’s favorite language is Python.
Sarah’s favorite language is C.
Phil’s favorite language is Python.
Edward’s favorite language is Ruby.

即便字典存储的是上千乃至上百万人的调查结果,这种循环也管用。

26.4.2 遍历字典中的所有键

在不需要使用字典中的值时,方法keys()很有用。下面来遍历字典favorite_languages,并
将每个被调查者的名字都打印出来:

favorite_languages = { 
    'jen': 'python', 
    'sarah': 'c', 
    'edward': 'ruby', 
    'phil': 'python', 
    }
# print(favorite_languages.keys())
for name in favorite_languages.keys():
    print(name.title())

Python用key()方法提取字典favorite_languages中的所有键,并依次将它们存储到变量name中。输出了每个被调查者的名字:

Jen
Sarah
Phil
Edward

遍历字典时,会默认遍历所有的键,因此,如果将上述代码中的for name in favorite_languages.keys():替换为for name in favorite_languages:,输出将不变。 如果显式地使用方法keys()可让代码更容易理解,你可以选择这样做,但如果你愿意,也可省略它。

在循环中,可使用当前键来访问与之相关联的值。下面来打印两条消息,指出两位朋友喜欢的语言。在名字为指定朋友的名字时,打印其喜欢的语言:

favorite_languages = { 
    'jen': 'python', 
    'sarah': 'c', 
    'edward': 'ruby', 
    'phil': 'python', 
    } 
friends = ['phil', 'sarah']
for name in favorite_languages.keys():
    print(name.title()) 
    if name in friends:
        print("  Hi " + name.title() +   ", Your favorite language is " + favorite_languages[name] + "!") 

我们创建了一个friends列表,其中包含我们要指出其喜欢的语言的朋友。
在循环中,我们打印每个人的名字,并检查当前的名字是否在列表friends中。如果在列表中,就打印一句问候语,我们使用了字典名,并将变量name的当前值作为键。:

Edward
Phil
Hi Phil, I see your favorite language is Python!
Sarah
Hi Sarah, I see your favorite language is C!
Jen

你还可以使用keys()确定某个人是否接受了调查。下面的代码确定Erin是否接受了调查:

favorite_languages = { 
    'jen': 'python', 
    'sarah': 'c', 
    'edward': 'ruby', 
    'phil': 'python', 
    } 
if 'erin' not in favorite_languages.keys():
    print("Erin, please take our poll!") 

方法keys()并非只能用于遍历;实际上,它返回一个列表,其中包含字典中的所有键,因此代码行只是核实’erin’是否包含在这个列表中。由于她并不包含在这个列表中,因此打印一条消息,邀请她参加调查:

Erin, please take our poll!

26.4.3 遍历字典中的所有值

如果只关心字典包含的值,可使用方法values(),它返回一个值列表,而不包含任何键。例如,如果我们想获得一个这样的列表,即其中只包含被调查者选择的各种语言,而不包含被调查者的名字,可以这样做:

favorite_languages = { 
    'jen': 'python', 
    'sarah': 'c', 
    'edward': 'ruby', 
    'phil': 'python', 
    } 
print("The following languages have been mentioned:") 
for language in favorite_languages.values(): 
    print(language) 

for语句提取字典中的每个值,并依次存储到变量language中。打印这些值:

The following languages have been mentioned:
Python
C
Python
Ruby

这种做法提取字典中所有的值,而没有考虑是否重复。涉及的值很少时,这也许不是问题,但如果被调查者很多,最终的列表可能包含大量的重复项。为剔除重复项,可使用集合(set)。
集合类似于列表,但每个元素都必须是独一无二的:

favorite_languages = { 
    'jen': 'python', 
    'sarah': 'c', 
    'edward': 'ruby', 
    'phil': 'python', 
    } 
print("The following languages have been mentioned:") 
for language in set(favorite_languages.values()):
    print(language) 

通过对包含重复元素的列表调用set(),可让Python找出列表中独一无二的元素,并使用这些元素来创建一个集合。。
结果是一个不重复的列表,其中列出了被调查者提及的所有语言:

The following languages have been mentioned:
Python
C
Ruby

随着你更深入地学习Python,经常会发现它内置的功能可帮助你以希望的方式处理数据。

26.5 set

setdict类似,是一组key的集合,但不存储value。由于key不能重复,所以,在set中,没有重复的key。

要创建一个set,需要提供一个list作为输入集合:

>>> s = set([1, 2, 3])
>>> s
{1, 2, 3}

注意,传入的参数[1, 2, 3]是一个list,而显示的{1, 2, 3}只是告诉你这个set内部有1,2,3这3个元素,显示的顺序也不表示set是有序的。。

重复元素在set中自动被过滤:

>>> s = set([1, 1, 2, 2, 3, 3])
>>> s
{1, 2, 3}

通过add(key)方法可以添加元素到set中,可以重复添加,但不会有效果:

>>> s.add(4)
>>> s
{1, 2, 3, 4}
>>> s.add(4)
>>> s
{1, 2, 3, 4}

通过remove(key)方法可以删除元素:

>>> s.remove(4)
>>> s
{1, 2, 3}

set可以看成数学意义上的无序和无重复元素的集合,因此,两个set可以做数学意义上的交集、并集等操作:

>>> s1 = set([1, 2, 3])
>>> s2 = set([2, 3, 4])
>>> s1 & s2
{2, 3}
>>> s1 | s2
{1, 2, 3, 4}

set和dict的唯一区别仅在于没有存储对应的value,但是,set的原理和dict一样,所以,同样不可以放入可变对象,因为无法判断两个可变对象是否相等,也就无法保证set内部“不会有重复元素”。试试把list放入set,看看是否会报错。

26.6 再谈不可变对象

上面我们讲了,str是不变对象,而list是可变对象。

对于可变对象,比如list,对list进行操作,list内部的内容是会变化的,比如:

>>> a = ['c', 'b', 'a']
>>> a.sort()
>>> a
['a', 'b', 'c']

而对于不可变对象,比如str,对str进行操作呢:

>>> a = 'abc'
>>> a.replace('a', 'A')
'Abc'
>>> a
'abc'

虽然字符串有个replace()方法,也确实变出了'Abc',但变量a最后仍是'abc',应该怎么理解呢?

我们先把代码改成下面这样:

>>> a = 'abc'
>>> b = a.replace('a', 'A')
>>> b
'Abc'
>>> a
'abc'

要始终牢记的是,a是变量,而'abc'才是字符串对象!有些时候,我们经常说,对象a的内容是'abc',但其实是,a本身是一个变量,它指向的对象的内容才是'abc'

┌───┐                  ┌───────┐
│ a │─────────────────>│ 'abc' │
└───┘                  └───────┘

当我们调用a.replace('a', 'A')时,实际上调用方法replace是作用在字符串对象'abc'上的,而这个方法虽然名字叫replace,但却没有改变字符串'abc'的内容。相反,replace方法创建了一个新字符串'Abc'并返回,如果我们用变量b指向该新字符串,就容易理解了,变量a仍指向原有的字符串'abc',但变量b却指向新字符串'Abc'了:

┌───┐                  ┌───────┐
│ a │─────────────────>│ 'abc' │
└───┘                  └───────┘
┌───┐                  ┌───────┐
│ b │─────────────────>│ 'Abc' │
└───┘                  └───────┘

所以,对于不变对象来说,调用对象自身的任意方法,也不会改变该对象自身的内容。相反,这些方法会创建新的对象并返回,这样,就保证了不可变对象本身永远是不可变的。

26.7 小结

dict在Python中非常有用,key必须是不可变对象,最常用的key是字符串。

tuple虽然是不变对象,但(1, 2, 3)可以作为dict或set的key,但(1,[2,3])不可以,试着解释结果。

猜你喜欢

转载自blog.csdn.net/acktomas/article/details/125948567
今日推荐