2016-11-16 74 views
12

我正在尝试编写Python表达式评估可视化器,它将显示如何逐步评估Python表达式(用于教育目的)。 Philip Guo的Python Tutor非常棒,但它逐行评估Python程序,并且我发现学生有时不明白如何评估单行表达式sorted([4, 2, 3, 1] + [5, 6])[1] == 2,并且想要将此过程可视化。 (似乎没有人去做,但 - 至少我什么也没发现。)理想的解决方案将创建的字符串这样的顺序:逐步跟踪Python表达式评估

sorted([4, 2, 3, 1] + [5, 6])[1] == 2 
sorted(>> [4, 2, 3, 1] + [5, 6] <<)[1] == 2 
>> sorted([4, 2, 3, 1, 5, 6]) << [1] == 2 
>> [1 2 3 4 5 6][1] << == 2 
>> 2 == 2 << 
True 

这里>><<被用来强调的是表达的一部分评估当前步骤,然后用其值替换。 (也许,我会稍后再尝试这个序列转换为某种动画。)

我目前的策略是使用ast.parse()解析字符串转换成AST,然后发现,将首先评估一个节点,eval(compile(node, '', 'eval'))对其进行评估(我绝对不希望重新实现整个Python :)),将评估结果转换为AST节点(使用repr,然后使用ast.parse()?),并将结果节点替换为当前节点,然后使用codegen.to_source生成修改后的代码字符串从(修改)AST并继续相同的过程,直到我在树中只有一个文字。

我的问题是:我如何找到一个将首先评估的节点?似乎我可以深入遍历树的子类ast.NodeVisitor,但我不确定如何检测到我到达了所需的节点,并且如何在它之后停止遍历?


编辑。

有可能我的初始方法与树的转换不可行。实际上,评估Python表达式的基本步骤并不是必须的,它必须将某个子表达式替换为更简单的子表达式(如在算术中)。例如,列表解析提供了一个非常复杂的行为,不能用来代替这个东西,然后递归地重复。所以我重申一个问题。我需要一些编程方法来展示如何逐步评估Python表达式。例如,@ jasonharper提到的MacroPy的tracing功能在此阶段是可接受的解决方案。不幸的是,MacroPy似乎被抛弃了,并且不适用于Python 3.是否有任何想法如何在不移植完整MacroPy的情况下类似于Python 3中的跟踪行为?


EDIT2。

就在我获得这个奖励后,我发现similar questiondebugger与非常接近的功能。然而,由于这个问题没有最终答案,而且我也不需要完整的调试器,所以我仍然在寻找一个可以在Jupyter环境中使用的答案。

回答

6

表达步进在Thonny IDE实现。

它使用AST仪器,其中每个(子)表达e转换为after(before(<location info>), e)。函数beforeafter是虚拟函数,用于在Python的跟踪系统中导致额外的调用 -events。这些额外的电话会在(子)表达评估即将开始或刚结束时通知。 (添加了类似的虚拟函数来检测每个语句的开始和结束。)

AST仪器和这些新事件的解释在thonny.backend.FancyTracer中完成。

Python的AST节点包含相应文本范围的起始位置,但它们有时不正确。完成位置完全丢失。 thonny.ast_utils.mark_text_ranges试图照顾这一点(但目前的解决方案不完整)。

如果有人从Thonny中提取相关的功能到一个更一般的包,那将会很好。也许甚至有两个软件包 - 一个用于计算Python AST的位置信息,另一个用于详细跟踪Python代码。如果有人带头,我愿意为此提供帮助。

1

添加这两个列表肯定不是该代码中要评估的第一个节点;我相信,其实早9个评估 - sorted4231[4,2,3,1]56[5,6]。您不仅需要确定执行哪些订单评估,还必须确定哪些评估值得展示。

我认为一个更好的方法是修改AST节点,以便它们发出它们之前/之后的状态作为执行的副作用。你不会关心他们的命令,你只需执行一次整个表达式。并且已经有一个名为macropy的软件包,它具有跟踪功能,可以完成此操作。它的输出并不完全符合你的要求,但它可能会被修改为更接近的匹配。

+0

Hm-m,'macropy''s [tracing](https://github.com/lihaoyi/macropy#tracing)看起来非常接近,很有前途,谢谢。我必须深入研究它。 –

+0

唉,'macropy'不适用于Python 3 :( –

2

为什么不使用dis模块?

由于CPython将Python编译为字节码并运行它,查看字节码可以让您最清楚实际发生的情况。

In [1]: import dis 

In [2]: dis.dis('sorted([4, 2, 3, 1] + [5, 6])[1] == 2') 
    1   0 LOAD_NAME    0 (sorted) 
       3 LOAD_CONST    0 (4) 
       6 LOAD_CONST    1 (2) 
       9 LOAD_CONST    2 (3) 
      12 LOAD_CONST    3 (1) 
      15 BUILD_LIST    4 
      18 LOAD_CONST    4 (5) 
      21 LOAD_CONST    5 (6) 
      24 BUILD_LIST    2 
      27 BINARY_ADD 
      28 CALL_FUNCTION   1 (1 positional, 0 keyword pair) 
      31 LOAD_CONST    3 (1) 
      34 BINARY_SUBSCR 
      35 LOAD_CONST    1 (2) 
      38 COMPARE_OP    2 (==) 
      41 RETURN_VALUE 

编辑:另一种方法可以是,以显示步骤中的一个接一个在IPython的:

In [1]: [4, 2, 3, 1] 
Out[1]: [4, 2, 3, 1] 

In [2]: [4, 2, 3, 1] + [5, 6] 
Out[2]: [4, 2, 3, 1, 5, 6] 

In [3]: sorted([4, 2, 3, 1, 5, 6]) 
Out[3]: [1, 2, 3, 4, 5, 6] 

In [4]: [1, 2, 3, 4, 5, 6][1] 
Out[4]: 2 

In [5]: 2 == 2 
Out[5]: True 
+0

从技术上讲,这是正确的。但是,我的问题的目的是创建一个工具,它可以帮助我在Python初学阶段教授学生,熟悉软件编程的基本概念,这不是计算机科学的学生,Python是他们的工具,而不是科学兴趣的对象,因此,向他们展示字节码是问题的答案是绝对不可能的。在这里?*。也许,有可能以某种方式解析字节码,以可访问的方式重建过程,但它看起来并不简单。 –

+0

@ IlyaV.Schurov由于您正在教学生的“第一步” Python,为什么要“封闭”呢?而且由于我是一个使用Python作为工具的培训机械工程师,我认为我可以说非计算机科学家有可能知道Pyth在作品上。 :-) –

+0

当然有可能。我既不是计算机科学家:)但我认为我们在开始时并不需要它。这有点像“在Facebook上登录之前,你必须学习x86架构,汇编,C,TCP/IP协议,HTTP协议,HTML,CSS和Javascript,然后发布你的照片。糟糕,停下来,我们忘了JPEG“。Anywhay,感谢这个想法,很好。 –