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

2024-10-17 02:37:51 发布

您现在位置:Python中文网/ 问答频道 /正文

我一直想换成Python3。令人惊讶的是,我的困难不在于模块或我自己的代码破坏。我的问题是,我总是在编写IPython代码时尝试和测试代码的不同方面,而在默认情况下有生成器会让这件事很恼火。我希望在我的知识上有一个缺口,或者有一些方法来解决这个问题。在

我的问题是:

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

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

奇怪的是,我一直发现我在引入bug(或无关的代码),这不是因为我不理解惰性求值,而是因为我在控制台中评估的内容不匹配,以及是什么导致它进入我的编辑器而从我的视图中溜走。在

我想马上做一件事:

  • 以某种方式配置IPython以强制某种严格的计算(除非我显式地关闭它)
  • 在不消耗发电机的情况下检查它(或者检查它然后重新启动?)在

Tags: 模块方法函数代码objectipython情况编辑器
2条回答

在一般情况下,预览或回放生成器是不可能的。这是因为生成器可能有副作用,你要么比预期的更早(预览时),要么多次(倒带前后)。例如,考虑以下生成器:

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

如果您可以预览此生成器生成的结果(12),您希望得到打印输出吗?在

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

考虑使用列表理解而不是生成器表达式。这在大多数情况下都很简单,只需在已有的genexp后面加上方括号。在许多将生成器传递给其他代码的情况下,任何iterable对象(例如list)都可以正常工作。在

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

还可以使用^{}获取两个(或更多)迭代器,这些迭代器将生成与传入的iterable相同的值。这将允许您检查其中一个的值,同时传递另一个。请注意,tee代码将需要存储任何迭代器生成的所有值,直到所有其他迭代器也生成它为止(因此,如果您在其他迭代器之前运行了一个迭代器,那么您最终使用的内存可能与使用list相同或更多)。在

如果这对其他人有帮助的话,这是IPython的一个线魔术,作为对答案的回应。它让它稍微不那么痛苦:

%ins <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

相关问题 更多 >