运行 来自 Python Shell、IPython 控制台和脚本内部时 Next 函数的不同行为

Different Behavior of Next Function when Ran from Python Shell, IPython Console and from Inside a Script

我有一个 class 定义了 next 方法。当我 运行 来自命令行的脚本创建此 class 和 运行 的对象时,以下循环调用 next,下一个 return 值未打印,但当我 运行 来自 Python 控制台的相同行时,将打印 return 值。

更具体地说,如果我们将以下脚本保存为 tests.py:

class Solution():
    def __next__(self):
        return 1

s = Solution()
for _ in range(5):
    next(s)

和运行 python test.py,没有打印任何东西(我在Python 3.4和一台Windows机器上测试过)。

但是,如果我们在Pythonshell中做如下操作,会打印输出:

(python34) C:\>python
Python 3.4.5 |Anaconda custom (64-bit)| (default, Jul  5 2016, 14:53:07) [MSC 
v.1600 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> class Solution():
...     def __next__(self):
...         return 1
...
>>> s = Solution()
>>> for _ in range(5):
...     next(s)
...
1
1
1 
1
1

在 IPython 控制台上也没有打印输出。

你可以看到here IPython 运行一个一个的ast节点,而不是一次全部,你可以改变IPython 运行 节点,但默认情况下,它只会在 AST 的最后一个根节点上触发显示副作用。如果你有一个 for 循环,"next()" 节点不是最后一个根节点,"for" 是,并且它 return 没有值(它是一个语句而不是表达式)。这是故意不让笔记本中的用户不知所措。

我猜这在语义上与 Python REPL 所做的不同,纯 Python repl 要么 运行 运行 交互 last 个节点,还是全部,不记得了。

你可以用更简单的例子检查 Python repl 做了什么:你不需要 Solution class,你可以用任何表达式来做,这确实有效:

>>> for i in range(5):
...     i # try adding more expressions like i**2 on another line
...
0
1
2
3
4

并且您可以在 IPython 中使用标志获得相同的行为:

$ ipython --TerminalInteractiveShell.ast_node_interactivity=all
Python 3.6.1 | packaged by conda-forge | (default, May 23 2017, 14:31:56)
Type 'copyright', 'credits' or 'license' for more information
IPython 6.2.0.dev -- An enhanced Interactive Python. Type '?' for help.

In [1]: for i in range(5):
   ...:     i
   ...:
Out[1]: 0
Out[1]: 1
Out[1]: 2
Out[1]: 3
Out[1]: 4

我个人喜欢 last_expr_or_assign 值,它使最后一个赋值 return 赋值:

$ipython--TerminalInteractiveShell.ast_node_interactivity=last_expr_or_assign

Python 3.6.1 |由 conda-forge 打包 | (默认,2017 年 5 月 23 日,14:31:56) 键入 'copyright'、'credits' 或 'license' 以获取更多信息 IPython 6.2.0.dev -- 增强的交互 Python。类型 '?'寻求帮助。

In [1]: a = 1 # No Out
   ...: b = 2 # Out will be 2. Useless here, 
   ...:       # but nice if you do `df = pd.load_csv(...)`
Out[1]: 2