python - 防止在迭代时修改自定义类

标签 python iterator concurrentmodification

如果我有一个带有接口(interface)的类:

class AnIteratable(object):

  def __init__(self):
    #initialize data structure

  def add(self, obj):
    # add object to data structure

  def __iter__(self):
    #return the iterator

  def next(self):
    # return next object

...我将如何设置以便如果在迭代过程中调用 add() 会抛出异常,类似于:

In [14]: foo = {'a': 1}

In [15]: for k in foo:
   ....:     foo[k + k] = 'ohnoes'
   ....:     
---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
<ipython-input-15-2e1d338a456b> in <module>()
----> 1 for k in foo:
      2     foo[k + k] = 'ohnoes'
      3 

RuntimeError: dictionary changed size during iteration

更新: 如果接口(interface)需要更多方法,请随意添加。我还删除了 __iter__() 的实现。

更新#2 根据 kindall 的回答,我模拟了以下伪实现。请注意,_datastructure 和索引到它的关联方法是抽象,类编写者必须编写他/她自己的数据结构遍历和位置指针机制。

class AnIteratable(object):

  def __init__(self):
    self._itercount = 0
    self._datastructure = init_data_structure() #@UndefinedVariable
    # _datastructure, and the methods called on it, are abstractions.

  def add(self, obj):
    if self._itercount:
      raise RuntimeError('Attempt to change object while iterating')
    # add object to data structure

  def __iter__(self):
    self._itercount += 1
    return self.AnIterator(self)

  class AnIterator(object):

    def __init__(self, aniterable):
      self._iterable = aniterable
      self._currentIndex = -1 #abstraction
      self._notExhausted = True

    def next(self):
      if self._iterable._datastructure.hasNext(self._currentIndex):
        self._currentIndex += 1
        return self._iterable._datastructure.next(self._currentIndex)
      else:
        if self._notExhausted:
          self._iterable._itercount -= 1
        self._notExhausted = False
        raise StopIteration

    def __next__(self):
      return self.next()

    # will be called when there are no more references to this object
    def __del__(self): 
      if self._notExhausted:
        self._iterable._itercount -= 1

更新 3 阅读更多之后,似乎 __del__ 可能不是正确的方法。以下可能是更好的解决方案,尽管它要求用户显式释放未用尽的迭代器。

    def next(self):
      if self._notExhausted and 
              self._iterable._datastructure.hasNext(self._currentIndex):
      #same as above from here

    def discard(self):
      if self._notExhausted:
        self._ostore._itercount -= 1
      self._notExhausted = False

最佳答案

您不应该将迭代器与实例混用。否则,当您想一次多次迭代实例时会发生什么?

想想你在哪里存储迭代器的位置。

将迭代器拆分为一个单独的类。创建迭代器实例时存储对象的大小。每当调用 next() 时检查大小

dicts 也不是万无一失的。您可以添加和删除一个键,这会搞砸迭代,但不会抛出错误

Python 2.7.3 (default, Aug  1 2012, 05:14:39) 
[GCC 4.6.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> d = {i:i for i in range(3)}
>>> d
{0: 0, 1: 1, 2: 2}
>>> for k in d:
...     d[k+3] = d.pop(k)
...     print d
... 
{1: 1, 2: 2, 3: 0}
{2: 2, 3: 0, 4: 1}
{3: 0, 4: 1, 5: 2}
{4: 1, 5: 2, 6: 0}
{5: 2, 6: 0, 7: 1}
{6: 0, 7: 1, 8: 2}
{7: 1, 8: 2, 9: 0}
{8: 2, 9: 0, 10: 1}
{9: 0, 10: 1, 11: 2}
{10: 1, 11: 2, 12: 0}
{11: 2, 12: 0, 13: 1}
{12: 0, 13: 1, 14: 2}
{13: 1, 14: 2, 15: 0}
{16: 1, 14: 2, 15: 0}
{16: 1, 17: 2, 15: 0}
{16: 1, 17: 2, 18: 0}

超过 3 次迭代!

关于python - 防止在迭代时修改自定义类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12469102/

相关文章:

具有稳定自动增量的 MySQL 原子插入(如果不存在)

java - 为什么列表的反向子列表的List.addAll导致ConcurrentModificationException

python - 内插数列

python - 将 Z3Py 与 Python 3.3 结合使用

java - Struts2 嵌套迭代器

python - SQLite、Python 和列表

c++ - 创建一个不可复制的 STL 迭代器是个好主意吗?

python - 如何正确分割代码串

python - 如何对数字字符串的python列表进行排序

JAVA java.util.ConcurrentModificationException :null Exception