源自《Python cookbook》第3版7.1节第2个例子:
import html def make_elements(name, value, **attrs): keyvals = [' %s="%s"' % item for item in attrs.items()] attr_str = '' .join(keyvals) element = '<{name}{attrs}>{value}</{name}>' .format( name = name, attrs = attr_str, value = html.escape(value)) return element make_elements('item' , 'Albatross' , size= 'large' , quantity= 6 ) make_elements('p' , '<spam>' )
程序的作用很简单,就是生成HTML标签,注意html这个模块只能在Python 3.x才有。
起初我只是注意到,生成标签属性列表的keyvals这个dictionary类型变量构建的方式很有意思,两个%s对应一个item,所以就查阅了相关的资料,结果扯出了挺多的东西,在此一并总结。
注:下面所有Python解释器使用的版本,2.x 对应的是2.7.3,3.x 对应的是3.4.1
在 Python 2.x 里,官方文档 里items的方法是这么说明:生成一个 (key, value) 对的list,就像下面这样:
>>> d = { 'size' : 'large' , 'quantity' : 6 } >>> d.items() [('quantity' , 6 ), ( 'size' , 'large' )]
在搜索的过程中,无意看到stackoverflow上这样一个问题:dict.items()和dict.iteritems()有什么区别? ,第一个答案大致的意思是这样的:
“起初 items() 就是返回一个像上面那样的包含dict所有元素的list,但是由于这样太浪费内存,所以后来就加入了(注:在Python 2.2开始出现的)iteritems(), iterkeys(), itervalues()这一组函数,用于返回一个 iterator 来节省内存,但是在 3.x 里items() 本身就返回这样的 iterator,所以在 3.x 里items() 的行为和 2.x 的 iteritems() 行为一致,iteritems()这一组函数就废除了。”
不过更加有意思的是,这个答案虽然被采纳,下面的评论却指出,这种说法并不准确,在 3.x 里 items() 的行为和 2.x 的 iteritems() 不一样,它实际上返回的是一个"full sequence-protocol object",这个对象能够反映出 dict 的变化,后来在 Python 2.7 里面也加入了另外一个函数 viewitems() 和 3.x 的这种行为保持一致
为了证实评论中的说法,我做了下面的测试,注意观察测试中使用的Python版本:
测试1(Python 2.7.3):
Python 2.7 . 3 (default, Feb 27 2014 , 19 : 58 : 35 ) [GCC 4.6 . 3 ] on linux2 Type "help" , "copyright" , "credits" or "license" for more information. >>> d = {'size' : 'large' , 'quantity' : 6 } >>> il = d.items() >>> it = d.iteritems() >>> vi = d.viewitems() >>> il [('quantity' , 6 ), ( 'size' , 'large' )] >>> it <dictionary-itemiterator object at 0x7fe555159f18 > >>> vi dict_items([('quantity' , 6 ), ( 'size' , 'large' )])
测试2(Python 3.4.1):
Python 3.4 . 1 (default, Aug 12 2014 , 16 : 43 : 01 ) [GCC 4.9 . 0 ] on linux Type "help" , "copyright" , "credits" or "license" for more information. >>> d = {'size' : 'large' , 'quantity' : 6 } >>> il = d.items() >>> it = d.iteritems() Traceback (most recent call last): File "<stdin>" , line 1 , in <module> AttributeError: 'dict' object has no attribute 'iteritems' >>> vi = d.viewitems() Traceback (most recent call last): File "<stdin>" , line 1 , in <module> AttributeError: 'dict' object has no attribute 'viewitems' >>> il dict_items([('size' , 'large' ), ( 'quantity' , 6 )])
可以看到在 Python 3.x 里面,iteritems() 和 viewitems() 这两个方法都已经废除了,而 item() 得到的结果是和 2.x 里面 viewitems() 一致的。
2.x 里 iteritems() 和 viewitems() 返回的内容都是可以用 for 来遍历的,像下面这样
>>> for k, v in it: ... print k, v ... quantity 6 size large >>> for k, v in vi: ... print k, v ... quantity 6 size large
这两者的区别体现在哪里呢?viewitems() 返回的是view object ,它可以反映出 dictionary 的变化,比如上面的例子,假如在使用 it 和 vi 这两个变量之前,向 d 里面添加一个key-value组合,区别就很容易看出来了。
>>> it = d.iteritems() >>> vi = d.viewitems() >>> d['newkey' ] = 'newvalue' >>> d {'newkey' : 'newvalue' , 'quantity' : 6 , 'size' : 'large' } >>> vi dict_items([('newkey' , 'newvalue' ), ( 'quantity' , 6 ), ( 'size' , 'large' )]) >>> it <dictionary-itemiterator object at 0x7f50ab898f70 > >>> for k, v in vi: ... print k, v ... newkey newvalue quantity 6 size large >>> for k, v in it: ... print k, v ... Traceback (most recent call last): File "<stdin>" , line 1 , in <module> RuntimeError: dictionary changed size during iteration
在第三行中,我们像 d 里面插入了一个新的元素,vi 可以继续遍历,而且新的遍历能够反映出 d 的变化,但是在遍历 it 的时候,报错提示 dictionary 在遍历的时候大小发生了变化,遍历失败。
总结起来,在 2.x 里面,最初是 items() 这个方法,但是由于太浪费内存,所以加入了 iteritems() 方法,用于返回一个 iterator,在 3.x 里面将 items() 的行为修改成返回一个 view object,让它返回的对象同样也可以反映出原 dictionary 的变化,同时在 2.7 里面又加入了 viewitems() 向下兼容这个特性。
所以在 3.x 里面不需要再去纠结于三者的不同之处,因为只保留了一个 items() 方法。