如何重写Python列表(迭代器)的行为?

6 投票
5 回答
10081 浏览
提问于 2025-04-16 07:30

运行这个:

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,但没有效果。)

我的真实使用案例:(如果有用可以看看;如果觉得没必要可以忽略后面的内容)

在对 iniparse 应用了一些补丁后,我发现了一个麻烦的副作用。我的 Undefined 类上设置了 __getattr__,它会返回一个新的 Undefined 对象。不幸的是,这意味着 list(iniconfig.invalid_section)(其中 isinstance(iniconfig, iniparse.INIConfig))会这样做(在 __getattr____getitem__ 中加上简单的 print):

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

等等,没完没了。

5 个回答

3

你可以抛出 IndexError 错误,而不是 KeyErrorKeyError 是用来处理像字典这样的映射类(比如 dict),而 IndexError 是用来处理序列的。

如果你在你的类中定义了 __getitem__() 方法,Python 会自动为它生成一个迭代器。而这个迭代器会在遇到 IndexError 时停止运行——具体可以参考 PEP234

3

正如@Sven所说,这个错误类型是不对的。但重点不是这个,重点是这样做是错误的,因为这不是你应该做的事情:阻止__getattr__抛出AttributeError意味着你已经覆盖了Python默认的检查对象是否有某个属性的方法,并用一个新的方法替代了它(ini_defined(foo.bar))。

但是Python已经有hasattr这个函数了!那为什么不直接用它呢?

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

如果你想要自定义遍历的方式,只需要在你的类里面定义一个 __iter__ 方法就可以了。

撰写回答