Python 迭代字典类对象

5
 class Test(object):

     def __init__(self, store):
         assert isinstance(store, dict)
         self.store = store

     def __getitem__(self, key):
         return self.store[key]

我尝试迭代这个类。在这个文档中说,实现__getitem__就足以迭代我的Test类。事实上,当我尝试迭代它时,它没有告诉我不能,但是我得到了一个KeyError

In [10]: a = Test({1:1,2:2})

In [11]: for i in a: print i
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-11-8c9c9a8afa41> in <module>()
----> 1 for i in a: print i

<ipython-input-9-17212ae08f42> in __getitem__(self, key)
      4         self.store = store
      5     def __getitem__(self, key):
----> 6         return self.store[key]
      7

KeyError: 0
  • 你知道这个 0 从哪里来的吗? (底层发生了什么)

我知道我可以通过添加一个 __iter__ 函数来解决它:

def __iter__(self):
    return dict.__iter__(self.store)
  • 这是解决问题的最佳方式吗?(我可能还会继承自字典类)。

最好的解决方法是什么问题,那个类应该做什么?这个类只有dict值吗? - vishes_shell
1个回答

8
您在找到的文档中遗漏了一个关键词:
对于序列类型,可接受的键应为整数和切片对象。[...]如果值不在序列的索引集合(在负值任何特殊解释后)中,则应引发IndexError。
注意:for循环希望对非法索引引发IndexError以允许正确检测序列的结尾。
我加粗了斜体强调。如果接受键而不是整数,则没有序列。
Python词汇表提供了更多解释,请参见sequence的定义
一个可迭代对象,支持使用 整数索引通过__getitem__()特殊方法进行高效元素访问,并定义了一个返回序列长度的__len__()方法。注意,dict也支持__getitem__()__len__(),但被视为映射而不是序列,因为查找使用的是任意不可变键而不是整数。

所以序列接受整数索引,这正是for在迭代时提供的内容*。当给定一个要迭代的对象时,如果除__getitem__之外没有其他手段可用,则会构造一个特殊的迭代器,该迭代器从0开始递增计数器,直到引发IndexError为止。在纯Python中,代码如下:

def getitem_iterator(obj):
    getitem = type(obj).__getitem__  # special method, so on the type
    index = 0
    try:
        while True:
            yield getitem(obj, index)
            index += 1
    except IndexError:
        # iteration complete
        return

实际的实现是用C编写的,请参见PySeqIter_Type定义和函数
请实现__iter__方法;当存在时,它将被使用。由于您包装了一个字典,因此可以简单地返回该字典的迭代器(使用iter()函数而不是直接调用特殊方法):
def __iter__(self):
    return iter(self.store)

* 严格来说,for 并不提供这个功能。它只是使用 iter(obj),当没有 __iter__ 方法可用时,那个调用 会产生特殊的迭代器。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接