如何覆盖Python列表(迭代器)行为?

时间:2010-11-23 13:14:16

标签: python

运行此:

class DontList(object):
    def __getitem__(self, key):
        print 'Getting item %s' % key
        if key == 10: raise KeyError("You get the idea.")
        return None

    def __getattr__(self, name):
        print 'Getting attr %s' % name
        return None

list(DontList())

产生这个:

Getting attr __length_hint__
Getting item 0
Getting item 1
Getting item 2
Getting item 3
Getting item 4
Getting item 5
Getting item 6
Getting item 7
Getting item 8
Getting item 9
Getting item 10
Traceback (most recent call last):
  File "list.py", line 11, in <module>
    list(DontList())
  File "list.py", line 4, in __getitem__
    if key == 10: raise KeyError("You get the idea.")
KeyError: 'You get the idea.'

如何更改,以便我获得[],同时仍允许访问这些密钥[1]等?

(我尝试过def __length_hint__(self): return 0,但没有帮助。)

我的真实用例:(如果它有用的话,请仔细阅读;可以随意忽略这一点)

a certain patch应用于iniparse之后,我发现了一个令人讨厌的副作用。将__getattr__设置在我的Undefined类上,该类返回一个新的Undefined对象。不幸的是,这意味着list(iniconfig.invalid_section)isinstance(iniconfig, iniparse.INIConfig)}正在执行此操作(在print__getattr__中添加简单__getitem__):

Getting attr __length_hint__
Getting item 0
Getting item 1
Getting item 2
Getting item 3
Getting item 4

等等无限期。

5 个答案:

答案 0 :(得分:7)

如果要覆盖迭代,则只需在类中定义__iter__方法

答案 1 :(得分:3)

只需举起IndexError而不是KeyErrorKeyError适用于类似地图的类(例如dict),而IndexError适用于序列。

如果在类上定义__getitem__()方法,Python将自动从中生成迭代器。迭代器终止于IndexError - 请参阅PEP234

答案 2 :(得分:3)

正如@Sven所说,这是错误的错误。但这不是重点,重点在于它已被破坏,因为它不是你应该做的事情:阻止__getattr__提升AttributeError意味着你已经覆盖了Python的默认方法测试对象是否具有属性并将其替换为新属性(ini_defined(foo.bar))。

但是Python已经有hasattr了!为什么不使用它?

>>> class Foo:
...     bar = None
...
>>> hasattr(Foo, "bar")
True
>>> hasattr(Foo, "baz")
False

答案 3 :(得分:1)

通过实现__iter__()方法覆盖您的类的迭代方式。迭代器通过引发StopIteration异常完成时发出信号,这是正常迭代器协议的一部分,不会进一步传播。以下是将其应用于示例类的一种方法:

class DontList(object):
    def __getitem__(self, key):
        print 'Getting item %s' % key
        if key == 10: raise KeyError("You get the idea.")
        return None

    def __iter__(self):
        class iterator(object):
            def __init__(self, obj):
                self.obj = obj
                self.index = -1
            def __iter__(self):
                return self
            def next(self):
                if self.index < 9:
                    self.index += 1
                    return self.obj[self.index]
                else:
                    raise StopIteration

        return iterator(self)

list(DontList())
print 'done'
# Getting item 0
# Getting item 1
# ...
# Getting item 8
# Getting item 9
# done

答案 4 :(得分:0)

我认为使用return iter([])是正确的方法,但让我们开始考虑list()的工作方式:

__iter__获取元素;如果收到StopIrteration错误则停止..然后获取该元素..

所以你只需yield __iter__ (x for x in xrange(0, 0))中的空生成器,例如iter([])),或只是{{1}}