如何在Python3中检查repl / ipython中的生成器

时间:2015-02-16 08:52:45

标签: python python-3.x ipython python-3.4

我一直在尝试切换到Python3。令人惊讶的是,我的困难不在于模块或我自己的代码破坏。我的问题是我在编写时总是在IPython中尝试和测试我的代码的不同方面,并且默认情况下使用生成器会让这令人生气。我希望我的知识存在差距,或者需要某种解决方法来解决这个问题。

我的问题是:

  • 每当我测试几行代码或函数并得到一个生成器时,我都不知道里面是什么,因为我得到了这样的响应:<generator object <genexpr> at 0x0000000007947168>。绕过它意味着我不能直接从我的编辑器中运行代码 - 我需要将输出转储到变量中和/或将其包装在列表中()。

  • 一旦我开始检查发电机,我要么消耗它(全部或部分),如果我想进一步测试它会混淆它。部分消费尤其令人讨厌,因为有时候我没有注意到后续代码会看到奇怪的结果。

奇怪的是,我一直在发现我正在引入错误(或无关的代码),不是因为我不理解懒惰的评估,而是因为我在控制台中评估的内容不匹配以及是什么让它进入了我的编辑在我的观点中滑落。

在我的头顶,我想做以下其中一项:

  • 以某种方式配置IPython以强制进行某种严格的评估(除非我明确地将其关闭)
  • 检查发电机而不消耗它(或者检查它然后重新启动?)

2 个答案:

答案 0 :(得分:3)

在一般情况下,您无法预览或倒带发电机。这是因为生成器可能有副作用,你要么早于预期(预览时),要么多次(在倒带之前和之后)。考虑以下生成器,例如:

def foo_gen():
    print("start")
    yield 1
    print("middle")
    yield 2
    print("end")

如果您可以预览此生成器(12)产生的结果,您是否也希望获得打印输出?

也就是说,可能有一些方法可以让您的代码更容易处理。

考虑使用列表推导而不是生成器表达式。这在大多数情况下非常简单,只需在您已经拥有的genexp周围放置方括号。在将生成器传递给其他代码的许多情况下,任何可迭代对象(例如list)都可以正常工作。

同样,如果您将生成器从其他位置传入代码,您通常可以将生成器传递给list并使用后面代码中的列表。这当然不是非常节省内存,因为你预先消耗整个生成器,但是如果你想在交互式控制台中看到这些值,那么这可能是必要的。

您还可以使用itertools.tee获取两个(或更多)迭代器,这些迭代器将产生与您传入的可迭代项相同的值。这将允许您从一个值中检查值,同时传递另一个值。请注意,tee代码需要存储由任何迭代器产生的所有值,直到它被所有其他迭代器生成(所以如果你运行一个迭代器远远超过其他迭代器,那么你最终可能会使用与使用list时相同或更多的内存。

答案 1 :(得分:1)

如果它对其他任何人有帮助,这对于IPython来说是一个神奇的线条,我把它放在一起回应答案。它让它变得不那么痛苦了:

%ins <var>将使用<var>制作两份itertools.tee副本。一个将被重新分配给<var>(因此您可以在其原始状态下重新使用它),另一个将被传递给print(list()),以便输出到终端。

%ins <expr>会将表达式传递给print(list())

要安装,请在ins.py

中将其另存为~/.ipython/profile_default/startup
from IPython.core.magic import register_line_magic

import itertools

@register_line_magic
def ins(line):
    if globals().get(line, None):
        gen1, gen2 = eval("itertools.tee({})".format(line))
        globals()[line] = gen2
        print(list(gen1))
    else:
        print(list(eval(line)))


# You need to delete this item from the namespace
del ins