交互式解释器为什么叫 REPL

今天着重介绍 Python 解释器的交互式模式。

什么是 REPL

REPL 是 4 个单词的首字母组:Read Eval Print Loop.

它表示一个循环中,在这个循环中不断的执行:

Read -> Eval -> Print -> Read -> Eval
-> Print -> Read -> Eval -> Print -> ...

所以更准确点的英文写法其实是 2 个单词: read-eval-print loop

![REPL wiki](images/REPL wiki.png)

  • Read,读取用户输入
  • Eval, 执行输入内容
  • Print,打印输出结果
  • Loop, 不断循环以上步骤

由上面的描述可知,我们经常用的命令行或 Shell 就是这种模式。不过一般提起 REPL 的时候,都是特指编程语言的交互式运行环境。

拥有交互式的编程环境对一个语言来说,特别是它的学习者来说,是一个非常大的帮助。

下面我们就重点聊聊 Python 的 REPL

进入 Loop

大概有这么几种方式能够进入 python 的交互式解释器模式:

  1. 不带任何参数运行 python,这是最常见的方式
  2. 通过 py 启动器运行解释器,这是 windows 下特有的方式
  3. 使用 python -i some_script.py,执行脚本后再进入,这个我们也在前面文章中介绍过了
  4. 其它 IDE(IDLE, PyCharm 等)内嵌的所谓 python shellpython console,它们也要依赖系统安装的 python

进入之后,首先看到的就是一段开场信息,然后就是 >>> 和一个闪烁的光标在等待你的输入。

冷门小知识:启动时打印的消息称为 banner想要自定义它? 得从源码编译 python。不过如果是嵌套实现,可以参考标准库 code 模块。事实上,一般 IDE 中内嵌的 Shell 多半都是在 code 模块之上封装的。

前面的符号 >>> 称为 提示符(Prompt String),我们在命令行中看到的 >$# 等都是提示符。

熟悉 Linux 系统的小伙伴可能知道,环境变量有一个 PS1,可以用来定义提示符的样式:

[root@host ~]# echo $PS1
[\u@\h \W]\$

这里的 1 代表序号,表示这是主提示符,后面还有 PS2PS3PS4

扯远了,咱还是回到 python。同样的,这里的 >>> 也是主提示符,它是保存在 sys.ps1 这个特殊变量里的,此外还有个 sys.ps2 的值是 ...

注意,提示符后面有 1 个空格。sys.ps1 = '>>> 'sys.ps2 = '... '

好玩的是,这两个变量是可以自定义的:

>>> sys.ps1 = 'DavyCloud Python 教程 2020 > '
DavyCloud Python 教程 2020 >
DavyCloud Python 教程 2020 > x = 1
DavyCloud Python 教程 2020 > print(x)
1

2 号提示符啥时候出场,继续看下面。

READ

一般情况下,按下回车键,就表示输入完毕,解释器就读取刚才输入的整个语句,开始执行了。大多数情况下,这些都是一行搞定。

如果从 Python 语法上讲,当前行没有结束,则还会继续等待输入,此时显示的就是 2 号提示符 ...,用户可以继续输入。

具体有以下几种情况:

复合语句块

例如:

  1. def
  2. class
  3. if / for / while
  4. try / except
  5. with

因为 python 不像其它语言那样用括号表示代码块的开始和结束,所以这种情况下,解释器无法判断何时代码块会结束。即使像下面这样挤在一行写也没用:

>>> x = 1
>>> if x == 1:pass
...
>>>
>>> def func(): return 1
...
>>>

开放的括号

例如:

>>> (1,
... 3,
... 4)
(1, 3, 4)
>>> {"name":
... 123, "key"
... :234}
{'name': 123, 'key': 234}

开放的三引号

普通的字符串是不行的,三引号的字符串则可以:

>>> "hello
  File "<stdin>", line 1
    "hello
         ^
SyntaxError: EOL while scanning string literal
>>> """hello
... xxx
... """
'hello\nxxx\n'
>>>

使用反斜杠 \ 结尾

在回车之前输入一个反斜杠 \,就能只换行而不完成输入,这招写代码的时候也一样适用。

值得注意的是,反斜杠 \ 之后不能再有任何字符,包括空格。否则都是语法错误。

Eval

EvalEvaluate 的简写。这一步就是解释器执行它读到的代码。

注意,python 里有 1 个内置函数 eval,它和这里的 Eval 可不完全一样。eval 只能执行 表达式(Expression),而这里解释器可以接受任何语句。从这一点看,解释器的 Eval 行为和内置的 exec 更贴近。

有一点需要特别提醒的是,如果输入的代码是一个表达式,那么交互式模式下,会把这个表达式的值存在一个特殊变量中,变量名是: _ (一个下划线)

Print

python 交互解释器的 Print 和内置函数 print 有一点细微差别:

当我们调用 print() 函数的时候,打印到屏幕上(即 stdout)的内容是 print() 函数写入的。print() 函数本身是没有任何结果返回的。

python 交互式解释器的 Print 只打印表达式的结果。所以呢,如果我们是在交互模式下输出 helloworld 是不需要调用 print() 函数的:

>>> "helloworld"
'helloworld'

细心的小伙伴会发现,这种方式打印出来的结果是带了引号的。

Tips:可以看到我们用的是双引号,而打印出来是单引号,可见 python 是优先使用 单引号表示字符串的。

这是因为在 python 里对象有 2 种生成文本表示的方法,1 个大家比较熟悉,是 str(),另一个则相对比较陌生了,是 repr()

print() 方法对应的是 str,即 print(obj) 等价于 print(str(obj))。而 REPL 中的 Print 对应的是 repr,即等价于 print(repr(obj))

写个简单的小例子体会下:

>>> print(repr("helloworld"))
'helloworld'

# 下面更复杂点的例子
>>> class C:
    def __repr__(self):
        return 'repr'
    def __str__(self):
        return 'str'

>>> c = C()
>>> c
repr
>>> print(c)
str
>>>

关于 strrepr 的细微差别,有机会可以再细聊。

除了字符串,还有其它各种对象都可以自动打印,所以在交互模式下看一个对象是什么非常方便。

在前面的示例中也给大家介绍了,我最常用的就是显示模块信息,来获取模块所在路径信息。

退出 Loop

在命令行中我们常用的退出命令是 exitquit,在 python 中输入它们会提示:

>>> quit
Use quit() or Ctrl-Z plus Return to exit
>>> exit
Use exit() or Ctrl-Z plus Return to exit

因为 python 接受的不是命令,只能接受函数,所以必需要带括号。同时还提示我们可以用快捷键 Ctrl-Z 退出。

这个快捷键是 Windows 系统下的,如果是 Linux 系统就应该是 Ctrl-D 了:

>>> quit
Use quit() or Ctrl-D (i.e. EOF) to exit
>>> exit
Use exit() or Ctrl-D (i.e. EOF) to exit
>>>

Ctrl-DLinux 系统中代表着 EOF(End Of File) 的含义,很多命令都支持这个快捷键结束。而 Ctrl-ZLinux 系统中是把任务挂入后台,实际并不会退出。

小提示,如果 Ctrl-Z 把进程挂到后台后,执行 fg 命令可恢复。fg(fore ground) 就是前台的意思。

另外值得提醒的是,exit()quit() 都支持传入一个整数作为程序的返回码,0 表示正常结束,非 0 值表示异常结束。

一道思考题:

在 python 解释器中执行 exit 的时候出现的提示需要带括号这种信息,这是如何做到的?

增强型 REPL

python 解释器的交互模式虽然很方便了,但是还有所不足。

Linux 系统的 shell 中,有很多非常方便的命令,一旦进入了 python shell,这些命令就都不能用了,特别是一些比较常用的比如 pwd 显示当前路径,ls 显示文件列表等。

虽然这些可以通过调用标准库提供的方法实现,但是没有直接命令来的方便。

如果说不能访问系统的 shell 命令这个不足有点牵强,那么缺少语法高亮,智能提示,自动补全等就显得比较难以接受了。

还有,虽然刚才我们提到了 _ 可以自动保存表达式的值,但是它只能保存最近依一次的。前面执行过的结果如果忘了存变量,就只能再来一次了。

优秀的程序员都是懒人,这些都不能忍!所以除了 python 官方自带的解释器,有很多第三方的增强型解释器出现,这里面最成功的就是 IPython

自带解释器上面提的所以不足之处,IPython 统统解决,并且做的更出色。

不仅如此,IPython 最终还进化成了一个跨语言的 Jyputer 项目。已经成为 python 在科学计算领域必不可少的工具了。

小结

介绍 REPL 的概念,以及 Python 解释器的交互式模式有哪些需要注意的地方。


文章内容虽基础,整理发布不轻松

如果看过有帮助,不妨 点赞 + 关注,谢谢!

Davy
Davy
学习📚 技术👨‍💻 投资📈

一些心得体会,希望能对你有所帮助🚀。