【问题标题】:In Python, is it a bad practice to "reset" an iterator when __iter__ is called?在 Python 中,调用 __iter__ 时“重置”迭代器是一种不好的做法吗?
【发布时间】:2018-10-04 16:54:07
【问题描述】:

例如,假设我有一个用于迭代文件中记录的类:

class MySpecialFile:
    ...
    def reset(self):
        self._handle.seek(0)

    def __iter__(self):
        self.reset()
        return self

编辑:

几个月后我才读到这个问题,我问了它,觉得有点愚蠢:)。

正如在下面的答案中所写,具有副作用的 __iter__ 方法是一件坏事。如果你想多次迭代你的对象,那么你只需要每次调用__iter__时返回一个新的迭代器对象:

class IterableFile:
    def __iter__(self):
       return FileIterator(self)

回想起来,这很明显,我想这就是我现在感到愚蠢的原因。不知道我在想什么,但我认为我不愿意拥有两个单独的类的原因是每个新的迭代器都需要创建一个新的文件处理程序(我的 SpecialFile 类只是文本/二进制文件之上的一个接口)当时我觉得这“过分”和奇怪。

【问题讨论】:

  • 这将无法跳过前几条记录(可能是标题行),然后使用for 循环迭代文件的其余部分。
  • 这会破坏各种事情。例如,itertools consume 配方,或使用 iter 来测试事物是否可迭代。看起来您正在尝试“修复”常见的文件上的两个循环错误,但这只会产生新问题。

标签: python iterator iterable


【解决方案1】:

iter 预计没有副作用。通过违反这个假设,您的代码会破坏各种事情。比如一个事物是否可迭代的标准测试:

try:
    iter(thing)
except TypeError:
    do_whatever()

将重置您的文件。同样,itertools consume recipe:

def consume(iterator, n=None):
    "Advance the iterator n-steps ahead. If n is None, consume entirely."
    # Use functions that consume iterators at C speed.
    if n is None:
        # feed the entire iterator into a zero-length deque
        collections.deque(iterator, maxlen=0)
    else:
        # advance to the empty slice starting at position n
        next(islice(iterator, n, n), None)

将产生不正确的文件位置,而不是在consume(your_file, n) 之后推进n 记录。在循环之前使用next 跳过前几条记录也会失败:

f = MySpecialFile(whatever)
next(f) # Skip a header, or try, anyway.
for record in f:
    # We get the header anyway.
    uhoh()

【讨论】:

  • 假设文件类比这更高级。不需要跳过header,打开文件时会自动读取(实际上是对象正常工作所需要的)。用户可以通过对象直接访问标题字段...也不需要跳过记录:文件对象允许根据对应用程序重要的唯一条件过滤和获取记录。另一方面,重复迭代很频繁。这就是“重置”存在的原因。
  • @sboby:你正在为自己创造一个雷区。也许您认为您不会做我在此答案中列出的任何事情。好吧,还有很多事情可以在迭代过程中调用iter。将您的文件对象安全地传递给任何不是专门为您的文件对象设计的库是不可能的,这严重限制了它的实用性。为什么不使用更安全的设计,例如让reset 返回文件并使用for record in f.reset() 明确重置而不破坏iter?
  • 哦,我不打算写那个代码。我会按照你说的去做(虽然我没有想过要在重置中返回 self,谢谢)或者从头开始重新考虑设计。但是......我的一部分说,用户犯错误的可能性要大得多,因为他/她会忘记重置迭代器。请记住,图书馆没有任何理智/有意义的使用意味着跳过记录。这就是我问这个问题的部分原因:它总是错了吗?该方案还将迭代器转换为可重复迭代的对象。
【解决方案2】:

任何颠覆语言结构预期流程的行为都是危险信号,比“代码气味”更糟糕。这不是 Python 特有的。它适用于任何语言或系统。

但是,请记住关于“小妖精”的限制:

  • 您的reset 是否改进了程序流程?
  • 生成的代码是否易于阅读和理解?

如果您已经为需要理解和维护这一点的人(包括您未来的自己)介绍了这些内容,那么reset 可能是一个很好的做法。我很怀疑,但我在工作中看到过这样的情况。

旁注:为什么在第一次构造迭代器时需要reset?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-09-09
    • 1970-01-01
    • 2011-12-22
    • 2021-12-31
    • 2023-03-19
    • 2015-05-31
    • 2019-01-24
    • 2020-08-23
    相关资源
    最近更新 更多