python/sage:如何获取最后一个表达式?

0 投票
2 回答
72 浏览
提问于 2025-04-14 16:15

如果我在Jupiter(无论是用Python还是Sage)里做一些事情,比如:

a = 42
b = 43
a + b

它会以某种方式理解这个过程返回的值是 a + b,也就是这里的85:

这里插入图片描述

同样,如果我只是这样做:

a = 42

它会理解这里没有什么需要返回的。

现在我想为一个不同的应用做类似的事情(缓存一个Python操作的结果)……我该如何获取这个信息,理想情况下只需运行代码并添加一些Python代码来获得这个信息?我尝试做:

a = 42
b = 43
a + b
print(_)

但这失败了。我在想做一些简单的事情,比如在最后一行前面加上 res = ,但如果最后一行缩进了,这可能会失败……我该如何优雅地获取这个信息呢?

2 个回答

2

在使用 ipython 的时候,有几种方法可以引用之前的 out 行:

In [113]: a = 42
     ...: b = 43
     ...: a + b
Out[113]: 85

In [114]: _     # also __ and ___
Out[114]: 85

In [115]: _113
Out[115]: 85

In [116]: Out[113]
Out[116]: 85

Out 是一个值的列表

如果你想引用一个在单元格中没有被赋值的行,是没有办法做到的:

a = 42
b = 43
a + b
print(?)

使用 a+b; 也会抑制最后的显示/保存,这和执行 c = a+b 是一样的。

_ 在普通的交互式 Python 会话中也能使用。jupyter/ipython 对这个历史记录的收集进行了增强。

1

我觉得这个问题之前已经在一个标题为“获取类似于Jupyter行为的Python变量的值和类型”的帖子中被问过了,发帖者已经在解决方案上取得了很大进展。

最终的方法是使用Python的exec()函数,先运行代码行,然后用eval()函数来计算最后一个表达式,就像Jupyter在默认设置下对InteractiveShell.ast_node_interactivity所做的那样。

我会在这里对我的答案进行一些修改和调整,以适应这里的具体代码情况:

lines = """a = 42
b = 43
a + b"""

exec(lines,globals(),locals())
print(eval(lines.split("\n")[-1]))

这会按预期打印出85的结果。

发帖者提到一个关于缩进的担忧。确实,我注意到我现在提出的解决方案在最后一行有缩进时会失败。不过我相信可以解决这个问题。如果最后一行有任何程度的缩进,我可以收集之前的行,直到缩进回到最外层的水平。然后用这些收集的行作为“最后一个表达式”来计算。不过我在这里没有这样做,因为不实现这一部分的解决方案已经相当清晰,我不想让它变得更加复杂。

确保阅读关于exec()eval()的警告,详细信息可以在我最初发布这个方法的地方找到。

针对一个评论,我还想强调,在答案中你可以进一步丰富功能,或者以其他方式结合Jupyter/IPython的能力到Python中,因为你可以进行导入。在我在“如何构建一个Python函数,其中输入是Python代码,输出是IPython丰富输出(HTML)?”这个线程中的答案里,我有一个部分叫“针对第一个评论的更新:”,我谈到了在Python中作为导入使用IPython来捕获RichOutput,这个方法在一个演示中进一步展开,展示了你可以利用IPython在Jupyter中使用的方法和函数进行的一些技巧。

*(我最终在这里调整了我之前的答案,因为我把它转移到这里。重新审视我在那里的答案时发现了一个问题,我没有正确分割以获取实际的最后一行进行计算。上次我运气好,原答案的最后一行只有一个字符,所以最后一行和最后一个字符在原始情况下是相同的。我现在也在那边修复了这个问题。)

撰写回答