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

如何重写Python列表(迭代器)行为?,python,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

运行此:

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.'

等等。

如果要覆盖迭代,只需在类中定义
\uu iter\uuu
方法

只需提升
索引器
,而不是
键错误
KeyError
用于映射类(例如
dict
),而
Indexer
用于序列


如果在类上定义方法,Python将自动从中生成迭代器。迭代器终止于
索引器
——请参阅。

通过实现
\uu iter\uuu()
方法重写类的迭代方式。当迭代器通过引发
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
class DontList(对象):
def _u获取项目(自身,密钥):
打印“正在获取项目%s”%key
如果key==10:raisekeyerror(“你明白了。”)
一无所获
定义(自我):
类迭代器(对象):
定义初始(自我,对象):
self.obj=obj
self.index=-1
定义(自我):
回归自我
def next(自我):
如果自指数<9:
自索引+=1
返回self.obj[自索引]
其他:
提出停止迭代
返回迭代器(self)
列表(DontList())
打印“完成”
#正在获取项目0
#获取项目1
# ...
#获取项目8
#获取项目9
#完成

我认为使用
返回iter([])
是正确的方法,但让我们开始思考
list()
的工作原理:

\uuuu iter\uuuu
获取元素;如果收到一个
StopIrteration
error stops

因此,您只需在
\uuuu iter\uuuuu
中生成一个空的生成器,例如
(x代表x范围(0,0))
,或者简单地
iter([])
,如前所述,这是错误的。但这不是重点,重点是这一点被打破了,因为这不是您应该做的事情:阻止
\uuuu getattr\uuuu
引发
AttributeError
意味着您已经覆盖了Python用于测试对象是否具有属性的默认方法,并将其替换为新的方法(
ini\u defined(foo.bar))

但是Python已经做到了!为什么不用呢

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

我曾经非常模糊地考虑过这个问题,但最终没有尝试,因为它没有显示出
获得attr\uuuu iter\uuuu
;显然,
hasattr
或它使用的任何东西都不会这样做。那么,您建议如何生产
[]
-
def\uuu iter\uuuuu(self):返回iter([])
(它可以工作,但它是正确的方法吗)?或者我想要通常的
类型错误:“Undefined”对象是不可编辑的
-有什么方法可以让我这样做(而不是通过自己产生错误来作弊)?iter通常会弹出下一个项目的存储,因此为什么生成器很难返回到前一个状态。你可以一直使用def uu iter uu(self):raise typeerror在仔细考虑了这一点和Sven的答案后,我想我想用
返回覆盖
\uu iter
;在
\uuu getitem\uuuu
中的
int
上产生
索引器,而不是提高
索引器,以使
未定义的
INI节的行为类似于已定义的节-类似于iniconfig中的节的
,以及节中的键的
,iniconfig中的
if部分
if键在部分
也应该起作用。@Chris Morgan:不明白你所说的
def(self)是什么意思:返回;产量
\uu iter\uu()
方法需要
返回
而不是
None
,然后使用
yield
或任何其他语句都不会做任何事情。也许你应该提交你自己的答案或更新你的问题,以显示你正在谈论的代码。@martineau:使用
yield
(在不可访问的代码中,但这并不重要)会使它返回一个生成器。我认为这是一个过火的行为……如果你想这样做(而OP不想这样做)只需提出StopIteration而不是KeyError in_getitem@Ant:也许吧。我写了一个完整的版本,因为我不确定OP是想在10点停止还是跳过它。使迭代器成为一个独立的对象,使它更加通用和灵活。不,这只是说明了它不会停止的问题。实际上,
def\uuuu iter\uuuu(self):返回;yield
非常有效。直观的例子是+1。然而,这并不意味着要直接使用。您必须嵌套“重载迭代器”,在我的问题中,我只需要一个类-只需要从
\uuuuiter\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuself)
方法返回一个合适的生成器。我通常不支持“不要那样做”的答案,但在这种情况下,Python并没有明确表示不应该这样做,但在实践中,它似乎从来没有得到很好的效果。我的直觉是,不要这样做。我认为此API更改将破坏任何使用
hasattr
测试未定义节中是否存在密钥的现有代码。需要使用未定义对象执行此操作的原因是,您可以设置INI值,而无需显式创建节。这就是iniparse的设计,这是正确的
ini_defined
只是我编写的一个简单函数,以便检查是否定义了节或值
中的code>'bar'也应该可以使用(真的,这更简洁,但它在未定义上的破坏方式与此列表()业务相同)-我想我会将
ini\u defined(foo.bar)
的所有用法改为foo中的
'bar'。克里斯:这几乎肯定是一个糟糕的设计,尽管我知道你可能不想更改它<有限公司
>>> class Foo:
...     bar = None
...
>>> hasattr(Foo, "bar")
True
>>> hasattr(Foo, "baz")
False