【问题标题】:Python: Weird behavior while using `yield from`Python:使用`yield from`时的奇怪行为
【发布时间】:2018-09-13 11:30:51
【问题描述】:

在下面的代码中,我遇到了RecursionError: maximum recursion depth exceeded

def unpack(given):
    for i in given:
        if hasattr(i, '__iter__'):
            yield from unpack(i)
        else:
            yield i

some_list = ['a', ['b', 'c'], 'd']

unpacked = list(unpack(some_list))

如果我使用some_list = [1, [2, [3]]],这可以正常工作,但当我尝试使用字符串时就不行了。

我怀疑我缺乏 Python 知识。任何指导表示赞赏。

【问题讨论】:

  • 字符串本身是可迭代的,它们迭代的东西是字符串。 for x in 'a' 产生 'a' 本身。
  • 尝试some_list = []; some_list.append(some_list); unpacked = list(unpack(some_list)) 看看深度>1000 的任何事物都可能发生这种情况。所以剩下的问题是为什么每个字符串的深度>1000,这是 wim 的回答(和 BallpointBen 的评论)解释的。
  • @abarnert 对于你的情况,是不是 __iter__ for list 返回自己,自然是无休止的递归?
  • @user7865286 是的——或者,也许更简单地说,列表包含自身:some_list[0] is some_list。我认为这不会比 s = 'a', s[0] is s 更令人惊讶,所以它有助于阐明问题,但现在我想一想,有多少人真正了解 Python 中的递归列表?唯一真正明显的例子是一个显式迭代自身的类,它太大且令人分心,不值得评论;最好像 BallpointBen 那样直接去s[0] is s 获取字符串。

标签: python yield


【解决方案1】:

字符串可以无限迭代。即使是一个字符的字符串也是可迭代的。

因此,除非您为字符串添加特殊处理,否则您将始终遇到堆栈溢出:

def flatten(x):
    try:
        it = iter(x)
    except TypeError:
        yield x
        return
    if isinstance(x, (str,  bytes)):
        yield x
        return
    for elem in it:
        yield from flatten(elem)

注意: 使用hasattr(i, '__iter__') 不足以检查i 是否可迭代,因为还有其他方法可以满足迭代器协议。 only 确定对象是否可迭代的可靠方法是调用 iter(obj)

【讨论】:

  • 可能只是一个评论。
  • "使用hasattr(i, '__iter__') 不足以检查i 是否可迭代" - 是的,但isinstance(x, Iterable) 在许多相同的情况下都失败了。最可靠的检查是致电iter
  • @user2357112 是对的。特别是,“旧式序列迭代协议”(实际上是一对相关的不完整协议,但没关系)没有被任何一个测试捕获。只需定义一个带有__len__ 的类,它返回一个int(并确保它在range(0, 1<<32) 中)和一个__getitem__,它不会为range(0, __len__()) 中的任何值引发IndexError,但会为@ 987654336@,你有一个可迭代但不是Iterable
  • 您认为实现flatten 的更Pythonic 方式是什么?我几乎总是更喜欢 EAFP,但不知何故,我总是为了这个而回到 LBYL。例如 - 要捕获哪些异常,TypeError 就够了吗?很难说。
  • isinstance(x, Iterable) 绝对是对hasattr(x, '__iter__') 的改进,至少。它会通过__iter__ = None 获取“反注册”,并在 Python 2 中检测到isinstance("asdf", Iterable)(尽管只是由于register 调用)。
猜你喜欢
  • 2014-07-08
  • 2016-01-22
  • 1970-01-01
  • 1970-01-01
  • 2014-11-16
  • 2016-03-19
  • 2018-04-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多