3.1 索引列表 VS 字典

我们已经看到,文本在 Python 中被视为一个词列表。链表的一个重要的属性是我们可以通过给出其索引来“看”特定项目,例如text1[100]。请注意我们如何指定一个数字,然后取回一个词。我们可以把链表看作一种简单的表格,如3.1所示。

Images/maps01.png

图 3.1:列表查找:一个整数索引帮助我们访问 Python 列表的内容。

对比这种情况与频率分布(3),在那里我们指定一个词然后取回一个数字,如fdist['monstrous'],它告诉我们一个给定的词在文本中出现的次数。用词查询对任何使用过字典的人都很熟悉。3.2展示一些更多的例子。

Images/maps02.png

图 3.2:字典查询:我们使用一个关键字,如某人的名字、一个域名或一个英文单词,访问一个字典的条目;字典的其他名字有映射、哈希表、哈希和关联数组。

在电话簿中,我们用名字查找一个条目得到一个数字。当我们在浏览器中输入一个域名,计算机查找它得到一个 IP 地址。一个词频表允许我们查一个词找出它在一个文本集合中的频率。在所有这些情况中,我们都是从名称映射到数字,而不是其他如列表那样的方式。总之,我们希望能够在任意类型的信息之间映射。3.1列出了各种语言学对象以及它们的映射。

表 3.1:

语言学对象从键到值的映射

  1. >>> pos = {}
  2. >>> pos
  3. {}
  4. >>> pos['colorless'] = 'ADJ' ![[1]](/projects/nlp-py-2e-zh/Images/7a979f968bd33428b02cde62eaf2b615.jpg)
  5. >>> pos
  6. {'colorless': 'ADJ'}
  7. >>> pos['ideas'] = 'N'
  8. >>> pos['sleep'] = 'V'
  9. >>> pos['furiously'] = 'ADV'
  10. >>> pos ![[2]](/projects/nlp-py-2e-zh/Images/6ac827d2d00b6ebf8bbc704f430af896.jpg)
  11. {'furiously': 'ADV', 'ideas': 'N', 'colorless': 'ADJ', 'sleep': 'V'}

所以,例如,[1]说的是 colorless 的词性是形容词,或者更具体地说:在字典pos中,键'colorless'被分配了值'ADJ'。当我们检查pos的值时[2],我们看到一个键-值对的集合。一旦我们以这样的方式填充了字典,就可以使用键来检索值:

  1. >>> pos['ideas']
  2. 'N'
  3. >>> pos['colorless']
  4. 'ADJ'

当然,我们可能会无意中使用一个尚未分配值的键。

  1. >>> pos['green']
  2. Traceback (most recent call last):
  3. File "<stdin>", line 1, in ?
  4. KeyError: 'green'

这就提出了一个重要的问题。与列表和字符串不同,我们可以用len()算出哪些整数是合法索引,我们如何算出一个字典的合法键?如果字典不是太大,我们可以简单地通过查看变量pos检查它的内容。正如在前面([2]行)所看到,这为我们提供了键-值对。请注意它们的顺序与最初放入它们的顺序不同;这是因为字典不是序列而是映射(参见3.2),键没有固定地排序。

换种方式,要找到键,我们可以将字典转换成一个列表[1]——要么在期望列表的上下文中使用字典,如作为sorted()的参数[2],要么在for 循环中[3]

  1. >>> list(pos) ![[1]](/projects/nlp-py-2e-zh/Images/7a979f968bd33428b02cde62eaf2b615.jpg)
  2. ['ideas', 'furiously', 'colorless', 'sleep']
  3. >>> sorted(pos) ![[2]](/projects/nlp-py-2e-zh/Images/6ac827d2d00b6ebf8bbc704f430af896.jpg)
  4. ['colorless', 'furiously', 'ideas', 'sleep']
  5. >>> [w for w in pos if w.endswith('s')] ![[3]](/projects/nlp-py-2e-zh/Images/934b688727805b37f2404f7497c52027.jpg)
  6. ['colorless', 'ideas']

注意

当你输入list(pos)时,你看到的可能会与这里显示的顺序不同。如果你想看到有序的键,只需要对它们进行排序。

与使用一个for循环遍历字典中的所有键一样,我们可以使用for循环输出列表:

  1. >>> for word in sorted(pos):
  2. ... print(word + ":", pos[word])
  3. ...
  4. colorless: ADJ
  5. furiously: ADV
  6. sleep: V
  7. ideas: N

最后,字典的方法keys()values()items()允许我们以单独的列表访问键、值以及键-值对。我们甚至可以排序元组[1],按它们的第一个元素排序(如果第一个元素相同,就使用它们的第二个元素)。

  1. >>> list(pos.keys())
  2. ['colorless', 'furiously', 'sleep', 'ideas']
  3. >>> list(pos.values())
  4. ['ADJ', 'ADV', 'V', 'N']
  5. >>> list(pos.items())
  6. [('colorless', 'ADJ'), ('furiously', 'ADV'), ('sleep', 'V'), ('ideas', 'N')]
  7. >>> for key, val in sorted(pos.items()): ![[1]](/projects/nlp-py-2e-zh/Images/7a979f968bd33428b02cde62eaf2b615.jpg)
  8. ... print(key + ":", val)
  9. ...
  10. colorless: ADJ
  11. furiously: ADV
  12. ideas: N
  13. sleep: V

我们要确保当我们在字典中查找某词时,一个键只得到一个值。现在假设我们试图用字典来存储可同时作为动词和名词的词 sleep:

  1. >>> pos['sleep'] = 'V'
  2. >>> pos['sleep']
  3. 'V'
  4. >>> pos['sleep'] = 'N'
  5. >>> pos['sleep']
  6. 'N'

最初,pos['sleep']给的值是'V'。但是,它立即被一个新值'N'覆盖。换句话说,字典中只能有'sleep'的一个条目。然而,有一个方法可以在该项目中存储多个值:我们使用一个列表值,例如pos['sleep'] = ['N', 'V']。事实上,这就是我们在4中看到的 CMU 发音字典,它为一个词存储多个发音。