Chinaunix首页 | 论坛 | 博客
  • 博客访问: 141618
  • 博文数量: 24
  • 博客积分: 2035
  • 博客等级: 大尉
  • 技术积分: 370
  • 用 户 组: 普通用户
  • 注册时间: 2006-05-15 07:34
文章分类

全部博文(24)

文章存档

2008年(24)

我的朋友

分类: Python/Ruby

2008-07-15 16:27:26

转载:http://www-128.ibm.com/developerworks/cn/opensource/os-python7/

如何成功地使用dictionary

级别:中级

Robert Brunner (), 研究科学家, National Center for Supercomputing Applications

2006 年 1 月 23 日

    本文研究Python类型层次结构并介绍dictionary容器类型。与前面文章中讨论的Python tuple、string和list容器类型不同,dictionary类型是一个无序的容器,依赖于键-值映射。因此,要根据键值访问dictionary中的元素,而不是根据它们在序列中的位置。dictionary类型的独特特性看起来可能不同寻常,但是如果使用得当,它们可以提供强大的能力。

dictionary

    我们都曾经使用过语言词典来查找不认识的单词的定义。语言词典针对给定的单词(比如python)提供一组标准的信息。这种系统将定义和其他信息与实际的单词关联(映射)起来。使用单词作为键定位器来寻找感兴趣的信息。这种概念延伸到Python编程语言中,就成了特殊的容器类型,称为dictionary。

    dictionary数据类型在许多语言中都存在。它有时候称为关联数组(因为数据与一个键值相关联),或者作为散列表。但是在Python中,dictionary是一个很好的对象,因此即使是编程新手也很容易在自己的程序中使用它。按照正式的说法,Python中的dictionary是一种异构的、易变的映射容器数据类型

创建dictionary

    本系列中前面的文章介绍了Python编程语言中的一些容器数据类型,包括tuple、string和list(参见 参考资料)。这些容器的相似之处是它们都是基于序列的。这意味着要根据元素在序列中的位置访问这些集合中的元素。所以,给定一个名为a的序列,就可以使用数字索引(比如a[0])或片段(比如a[1:5])来访问元素。Python中的dictionary容器类型与这三种容器类型的不同之处在于,它是一个无序的集合。不是按照索引号,而是使用键值来访问集合中的元素。这意味着构造dictionary容器比tuple、string或list要复杂一些,因为必须同时提供键和相应的值,如清单1所示。


>>> d = {0: 'zero', 1: 'one', 2 : 'two', 3 : 'three', 4 : 'four', 5: 'five'}
>>> d
{0: 'zero', 1: 'one', 2: 'two', 3: 'three', 4: 'four', 5: 'five'}
>>> len(d)
>>> type(d) # Base object is the dict class

>>> d = {} # Create an empty dictionary
>>> len(d)
>>> d = {1 : 'one'} # Create a single item dictionary
>>> d
{1: 'one'}
>>> len(d)
>>> d = {'one' : 1} # The key value can be non-numeric
>>> d
{'one': 1}
>>> d = {'one': [0, 1,2 , 3, 4, 5, 6, 7, 8, 9]}
>>> d
{'one': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]}


    如这个例子所示,在Python中创建dictionary要使用花括号和以冒号分隔的键-值组合。如果没有提供键-值组合,那么就会创建一个空的dictionary。使用一个键-值组合,就会创建具有一个元素的 dictionary,以此类推,直至您需要的任何规模。与任何容器类型一样,可以使用内置的len方法查明集合中元素的数量。

    前面的示例还演示了关于dictionary容器的另一个重要问题。键并不限制为整数;它可以是任何不易变的数据类型,包括integer、float、tuple或string因为list是易变的,所以它不能作为dictionary中的键。但是dictionary中的值可以是任何数据类型的。

    最后,这个示例说明了Python中dictionary的底层数据类型是dict对象。要进一步了解如何使用 Python中的dictionary,可以使用内置的帮助解释器来了解dict类,如清单2所示。


>>> help(dict)on class dict in module __builtin__:
     dict(object)
| dict() -> new empty dictionary.
| dict(mapping) -> new dictionary initialized from a mapping object


    关于dict类的帮助指出,可以使用构造函数直接创建dictionary,而不使用花括号。既然与其他容器数据类型相比,在创建dictionary时必须提供更多的数据,那么这些创建方法比较复杂也就不足为奇了。但是,在实践中使用dictionary并不难,如清单3所示。


>>> l = [0, 1,2 , 3, 4, 5, 6, 7, 8, 9]
>>> d = dict(l)(most recent call last):
 File "", line 1, in ?: can't convert dictionary
 update sequence element #0 to a sequence
   
>>> l = [(0, 'zero'), (1, 'one'), (2, 'two'), (3, 'three')]
>>> d = dict(l)
>>> d
{0: 'zero', 1: 'one', 2: 'two', 3: 'three'}
>>> l = [[0, 'zero'], [1, 'one'], [2, 'two'], [3, 'three']]
>>> d
{0: 'zero', 1: 'one', 2: 'two', 3: 'three'}
>>> d = dict(l)
>>> d
{0: 'zero', 1: 'one', 2: 'two', 3: 'three'}
>>> d = dict(zero=0, one=1, two=2, three=3)
>>> d
{'zero': 0, 'three': 3, 'two': 2, 'one': 1}
>>> d = dict(0=zero, 1=one, 2=two, 3=three): keyword can't be an expression


    可以看到,创建dictionary需要键值和数据值。第一次从list创建dictionary的尝试失败了,这是因为没有匹配的键-数据值对。第二个和第三个示例演示了如何正确地创建dictionary:在第一种情况下,使用一个list,其中的每个元素都是一个tuple;在第二种情况下,也使用一个list,但是其中的每个元素是另一个list。在这两种情况下,内层容器都用于获得键到数据值的映射。

    直接创建dict容器的另一个方法是直接提供键到数据值的映射。这种技术允许显式地定义键和与其对应的值。这个方法其实用处不大,因为可以使用花括号完成相同的任务。另外,如前面的例子所示,在采用这种方式时对于键不能使用数字,否则会导致抛出一个异常。

访问和修改dictionary

    创建了dictionary之后,需要访问其中包含的数据。访问方式与访问任何Python容器数据类型中的数据相似,如清单4所示。


>>> d = dict(zero=0, one=1, two=2, three=3)
>>> d
{'zero': 0, 'three': 3, 'two': 2, 'one': 1}
>>> d['zero']
>>> d['three']
>>> d = {0: 'zero', 1: 'one', 2 : 'two', 3 : 'three', 4 : 'four', 5: 'five'}
>>> d[0]
'zero'
>>> d[4]
'four'
>>> d[6](most recent call last):
 File "", line 1, in ?: 6
>>> d[:-1](most recent call last):
 File "", line 1, in ?: unhashable type
 


    可以看到,从dictionary中获取数据值的过程几乎与从任何容器类型中获取数据完全一样。在容器名后面的方括号中放上键值。当然,dictionary可以具有非数字的键值,如果您以前没有使用过这种数据类型,那么适应这一点需要些时间。因为在dictionary中次序是不重要的(dictionary中数据的次序是任意的),所以可以对其他容器数据类型使用的片段功能,对于dictionary是不可用的。试图使用片段或者试图从不存在的键访问数据就会抛出异常,指出相关的错误。

    Python中的dictionary容器也是易变的数据类型,这意味着在创建它之后可以修改它。如清单5所示,可以添加新的键到数据值的映射,可以修改现有的映射,还可以删除映射。


>>> d = {0: 'zero', 1: 'one', 2: 'two', 3: 'three'}
>>> d[0]
'zero'
>>> d[0] = 'Zero'
>>> d
{0: 'Zero', 1: 'one', 2: 'two', 3: 'three'}
>>> d[4] = 'four'
>>> d[5] = 'five'
>>> d
{0: 'Zero', 1: 'one', 2: 'two', 3: 'three', 4: 'four', 5: 'five'}
>>> del d[0]
>>> d
{1: 'one', 2: 'two', 3: 'three', 4: 'four', 5: 'five'}
>>> d[0] = 'zero'
>>> d
{0: 'zero', 1: 'one', 2: 'two', 3: 'three', 4: 'four', 5: 'five'}


    清单5演示了几个重点。首先,修改数据值是很简单的:将新的值分配给适当的键。其次,添加新的键到数据值的映射也很简单:将相关数据分配给新的键值。Python自动进行所有处理。不需要调用append这样的特殊方法。对于dictionary容器,次序是不重要的,所以这应该好理解,因为不是在dictionary后面附加映射,而是将它添加到容器中。最后,删除映射的办法是使用del操作符以及应该从容器中删除的键。

    在清单5中有一个情况看起来有点儿怪,键值是按照数字次序显示的,而且这个次序与插入映射的次序相同。不要误解——情况不总是这样的。Python dictionary中映射的次序是任意的,对于不同的Python安装可能会有变化,甚至多次使用同一Python解释器运行相同代码也会有变化。如果在一个dictionary中使用不同类型的键和数据值,那么就很容易看出这一点,如清单6所示。


>>> d = {0: 'zero', 'one': 1}
>>> d
{0: 'zero', 'one': 1}
>>> d[0]
'zero'
>>> type(d[0])

>>> d['one']
>>> type(d['one'])

>>> d['two'] = [0, 1, 2]
>>> d
{0: 'zero', 'two': [0, 1, 2], 'one': 1}
>>> d[3] = (0, 1, 2, 3)
>>> d
{0: 'zero', 3: (0, 1, 2, 3), 'two': [0, 1, 2], 'one': 1}
>>> d[3] = 'a tuple'
>>> d
{0: 'zero', 3: 'a tuple', 'two': [0, 1, 2], 'one': 1}


    如这个例子所示,可以在一个dictionary中使用不同数据类型的键和数据值。还可以通过修改 dictionary添加新的类型。最后,产生的dictionary的次序并不与插入数据的次序匹配。本质上,dictionary中元素的次序是由Python dictionary数据类型的实际实现控制的。新的Python解释器很容易改变这一次序,所以一定不要依赖于元素在dictionary中的特定次序。

用dictionary进行编程

    作为正式的Python数据类型,dictionary支持其他较简单数据类型所支持的大多数操作。这些操作包括一般的关系操作符,比如 <、> 和 ==,如清单7所示。


>>> d1 = {0: 'zero'}
>>> d2 = {'zero':0}
>>> d1 < d2
>>> d2 = d1
>>> d1 < d2
>>> d1 == d2
>>> id(d1)
>>> id(d2)
>>> d2 = d1.copy()
>>> d1 == d2
>>> id(d1)
>>> id(d2)


    前面的示例创建两个dictionary并使用它们测试<关系操作符。尽管很少以这种方式比较两个 dictionary;但是如果需要,可以这样做。

    然后,这个示例将赋值给变量d1的dictionary赋值给另一个变量d2。注意,内置的id()方法对于d1和 d2返回相同的标识符值,这说明这不是复制操作。要想复制dictionary,可以使用copy()方法。从这个示例中的最后几行可以看出,副本与原来的dictionary完全相同,但是容纳这个dictionary的变量具有不同的标识符。

    在Python程序中使用dictionary时,很可能希望检查dictionary中是否包含特定的键或值。如清单 8所示,这些检查很容易执行。


>>> d = {0: 'zero', 3: 'a tuple', 'two': [0, 1, 2], 'one': 1}
>>> d.keys()
[0, 3, 'two', 'one']
>>> if 0 in d.keys():
... print 'True'
...
>>> if 'one' in d:
... print 'True'
...
>>> if 'four' in d:
... print 'Dictionary contains four'
... elif 'two' in d:
... print 'Dictionary contains two'
... contains two


    测试dictionary中键或数据值的成员关系是很简单的。dictionary容器数据类型提供几个内置方法,包括keys()方法和values()方法(这里没有演示)。这些方法返回一个列表,其中分别包含进行调用的 dictionary中的键或数据值。

    因此,要判断某个值是否是dictionary中的键,应该使用in操作符检查这个值是否在调用keys()方法所返回的键值列表中。可以使用相似的操作检查某个值是否在调用values()方法所返回的数据值列表中。但是,可以使用dictionary名作为简写表示法。这是有意义的,因为一般希望知道某个数据值(而不是键值)是否在dictionary中。

    在“Discover Python, Part 6” 中,您看到了使用for循环遍历容器中的元素是多么容易。同样的技术也适用于Python dictionary,如清单9所示。


>>> d = {0: 'zero', 3: 'a tuple', 'two': [0, 1, 2], 'one': 1}
>>> for k in d.iterkeys():
... print d[k]
... tuple
[0, 1, 2]
>>> for v in d.itervalues():
... print v
... tuple
[0, 1, 2]
>>> for k, v in d.iteritems():
... print 'd[',k,'] = ',v
... [ 0 ] = zero[ 3 ] = a tuple[ two ] = [0, 1, 2][ one ] = 1


    这个示例演示了遍历dictionary的三种方式:使用从iterkeys()、itervalues()或iteritems() 方法返回的Python迭代器。(顺便说一下,可以通过在dictionary上直接调用适当方法,比如 d.iterkeys(),从而检查这些方法是否返回一个迭代器而不是容器数据类型。)iterkeys()方法允许遍历 dictionary的键,而itervalues()方法允许遍历dictionary包含的数据值。另一方面,iteritems() 方法允许同时遍历键到数据值的映射。

dictionary:另一种强大的Python容器

    本文讨论了Python dictionary数据类型。dictionary是一种异构的、易变的容器,依赖键到数据值的映射(而不是特定的数字次序)来访问容器中的元素。访问、添加和删除dictionary中的元素都很简单,而且dictionary很容易用于复合语句,比如if语句或for循环。可以在dictionary中存储所有不同类型的数据,可以按照名称或其他复合键值(比如tuple)访问这些数据,所以Python dictionary使开发人员能够编写简洁而又强大的编程语句。


学习
  • 您可以参阅本文在 developerWorks 全球站点上的 英文原文

  • 阅读 developerWorks “探索 Python” 系列中的所有文章。

  • 如果有了 Python 解释器, 可以帮助您开始学习这种语言。

  • 访问 developerWorks 开放源码专区,这里有丰富的 how-to 信息、工具和项目更新,可以帮助您利用开放源码技术进行开发并将其用于 IBM 产品。

获得产品和技术


  • 使用 IBM 试用软件 改进您的下一个开放源码开发项目,这些软件可以下载或者通过 DVD 获得。

讨论


    Robert J. Brunner是国家超级计算应用中心的研究科学家,而且是伊利诺伊大学香槟分校的天文学助理教授。他曾就许多主题出版了好几本书,撰写了许多文章和教程。











阅读(2467) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~