在这篇文章里,我会来阐述下为什么我觉得 Python 对于计算机编程入门教学来说是一门很棒的编程语言(对基础编程课程更多观点可以查看这篇文章)。这也是从我针对初学者Python 编程教学过程中获得并总结的一些心得体会。

简单地说,我有足够的信心作出这样一个判断,那就是对于大部分的编程初学者来说他们并不是很关心计算机科学或编程理论。对他们来说,重要的是如何简单地通过尽可能少的努力编写尽可能少的代码并在计算机上先运行起来。需要写的代码越少,可能出现的错误和漏洞也就越少,从而在关键的早期学习过程中减少学习障碍。很多初学者会因这种障碍带来挫折感并丧失信心,甚至会完全放弃编程学习。

Python 在编程入门学习方面优于其他的很多编程语言,因为它可以让初学者较少地关注诸如类型、编译器和编写样板代码等细节,把重心放到与实现特定目标相关的算法和数据结构的学习(类似的语言还有Ruby)。下面我会列举 Python 对于初学者友好的一些特点,这些特点很适合初学者进行编程入门学习。本文内容包含以下几个部分:

1. 无需声明变量类型
2. 运行期类型错误,而不是编译期类型错误
3. print 语句是多态的,会自动插入空格与换行
4. 字符串、列表、字典内置,且语法统一
5. 漂亮、干净的编程语法,简洁而准确;不会因为简洁而造成混淆
6. 交互式命令提示符,和无需重启会话即可动态重新加载模块的能力
7. 返回新值,而不是改变现有对象的操作
8. 全面的标准库,以及大量可用的免费第三方库
9. 合适程度的运算符重载

1. 无需声明变量类型

当一个初学者刚开始学习类似 C++ 或者 Java 这种静态类型的编程语言时,通常他/她写的第一行代码便是像 int count 或者 List<String> seq 这种变量声明。这些代码除了让你视觉上感觉混乱、模糊真实的执行动作语句之外,没有别的用处。在初学者通常需要编写的小规模程序代码中,这些声明代码甚至会达到总代码量的 1/41/3 之多。对于经验丰富的程序员来说声明变量和类型是必要的也是自然的,但对于尚在努力应对编译器类型错误信息的初学者而言,它们确实很烦人。(在某些静态类型编程语言中你也可以不必声明类型,比如 ML 和其他有类型判断的语言,但是第一次就教学生 ML 语言就像是一上来就要给人家鼻子上狠狠来上一拳一样。)。

2. 运行期类型错误,而不是编译期类型错误

编译期类型错误会给初学者带来很大困扰。至少让代码先跑起来,执行一些操作,这比连编译都通过不了,根本运行不起来要强很多。虽然在以产品质量为目标的代码规范中,严格的类型检查是非常重要的,因为这样可以减少因为运行期类型错误而导致的问题。但是对于初学者而言,他们并不会去写核反应堆代码,他们写的只是一二十行玩具操控的代码。至少让他们的程序先运行起来,让他们在运行期知道哪一行代码出了问题。有时候在发生这样的错误之前程序已然运行很深,运行期错误处理可以让初学者对代码错误有更直观的认识。

对于一个学习编程的新手来说,只是希望他的代码能够运行并做点什么(即使代码并不完美),这时候最打击士气的莫过于一个叫做「编译器」的神秘暴君拒绝将他的代码变成可执行程序。理想状态下,学生越频繁地运行他们的代码,缩短编程期间编辑调试的迭代周期增加迭代次数,编程学习的动力就越大。

(布鲁斯·埃凯尔在 强类型与强测试 文章中讨论了动态类型语言在生成高质量代码方面的优势,即便对于资深程序员来说也是如此。)

3. print 语句是多态的,会自动插入空格与换行

新手会在程序输出与调试代码中大量使用 print 语句(毕竟就让他们使用 Python 的调试器会有点困难)。不像 C 语言风格中晦涩的 printf 的语法,Python的 print 可以正确地打印出所有类型的变量,而无需考虑是基础变量还是复合变量。相比于下面的古怪的代码:

printf("%s lives at %d %s\n",
   person, street_number, street_name);

学生可以写出如下更容易理解的清晰代码:

print person, "lives at", street_number, street_name

上面的 Python print 语句实际上所有人都容易理解,就是要打印出一行像「约翰·多伊住在榆树街 135 号」这样的信息;而 C printf 的版本只适合 C 程序员阅读。

4. 字符串、列表、字典内置,且语法统一

即使作为初级程序员,也可以很容易的通过使用字符串(存储文本数据)、列表(存储序列数据)、字典(存储关联数据)写出很棒的代码。Python 通过统一的操作语法让这三个基础的数据结构变得极其容易操作, 比如:使用 [...] 进行元素访问,使用 for ... in ... 来循环迭代,使用 if ... in ... 来做成员测试。

动态类型可以很容易的避免列表、字典笨拙的长类型声明。比如避免某些晦涩的 Map<String, List<Integer>> 声明, 而使用简单的初始化像 x = [1, 2, 3] 就可以简单赋值。例如你想创建一个 key 为字符串类型,value 为数值类型的字典,只需要把对应的内容扔进去就好,而不需要做任何冗长的声明:

d = {}
d["my bowling scores"] = [120, 140, 165]

而要实现相同的初始化功能,Java 1.5 的代码却是这样的:

Map<String, List<Integer>> d = new HashMap<String, List<Integer>>();
List<Integer> lst = new LinkedList<Integer>();
lst.add(120);
lst.add(140);
lst.add(165);
d.put("my bowling scores", lst);

我对 Java 没有意见(虽然可怜的 Java 总会收到抨击),但看看这一堆冗长的代码。为了将把相似的数据填到字典中,显然至少需要多次的重复代码输入。

是的,我知道,对于一个从事大型软件工作的经验丰富的程序员来说,多输入几行代码无关紧要。但对于初学者呢?哪个版本的代码更容易被理解!我无法想象怎样向一个初学者解释这段  Java 代码。对于初学者,他们并不需要关心类型声明,Java 泛型,参数多态机制或任何其他奇怪的编程语言术语,他们需要的只是希望用尽可能少的代码去实现自己预期的想法而已。

5. 漂亮、干净的编程语法,简洁而准确;不会因为简洁而造成混淆

毫无疑问 Python 代码看起来优雅、干净、容易书写,并且易读、易理解。但同时,Python 也不会因为这种简洁造成混淆,出现像 Perl 代码经常会出现的情况。它在简洁和清楚准确两个层面上做到了很好的平衡。

最初 Python 吸引我的地方是我发现写 Python 代码很有趣,因为它看起来很美观。虽然这听起来有一点老套,但是看 Python 代码时唤起的愉快的发自内心的情感可以提高程序员的工作效率,让他/她觉得代码更整洁,更容易理解。如果你需要每天长时间盯着代码好几个小时,那么它越看起来越优雅就越好(世界上大部分事情都是如此),并且这一点很重要。

6. 交互式命令提示符,和无需重启会话即可动态重新加载模块的能力

无论何时一个编程语言初学者有关于一个函数或者语言特色是如何工作的问题时,最好是使用科学的方法来进行回答,那就是先去试试看看发生了什么,然后再去改善,润色自己的理解,再去重新实验。最佳实践就是通过交互式命令提示,因为不需要保存源文件,重新编译它,重新编辑来消除你刚刚修改的代码中愚蠢的类型错误,重新编译,然后重新执行。通过使用 Python,你可以在交互式提示中创建数据结构,并实时测试它们。

更好的是,当你想去测试写在源文件( Python 中称之为模块)的函数,你可以使用 reload 函数来重新加载你的改动而无需退出交互式环境。这对于在运行试验模块前需要做很多准备工作的场景十分的有帮助。一个快速的编辑调试周期会使初学者受益颇多,因为这会让他们感觉测试起代码来更自由,代价更低。实验的越多,理解的越深,越有学下去的动力。

7. 返回新值,而不是改变现有对象的操作

Python 标准库的许多操作都是以函数式风格编写的,这意味着这些操作会返回新值而不是修改原来的对象。在教授编程初学者使用 Python 的交互式命令提示符时,我发现这个函数特点非常有用,因为它允许人们在不改变原对象的情况下继续对同一对象进行重复实验。

例如,当我教授学生关于字符串切片的内容(目标是去除字符串两边的双引号)时,我创建了一个值为 Weight in kilogramsx 字符串类型变量,为了演示字符串切片的语法,我让他们尝试用不同的索引切片,以便自己查看结果:

>>> print x
"Weight in kilograms"
>>> print x[1:]
Weight in kilograms"
>>> print x[2:]
eight in kilograms"
>>> print x[3:]
ight in kilograms"
>>> print x[:-1]
"Weight in kilograms
>>> print x[:-2]
"Weight in kilogram
>>> print x[:-3]
"Weight in kilogra
>>> print x[1:-1]
Weight in kilograms

最后一行代码实现了去除首部和尾部双引号的需求,因为切片操作每次返回的都是新值,而原始的字符串并没有被改变,所以可以被无限次操作而不受影响。试想如果不是这样,测试切片操作时每次都需要重新修改原始字符串的内容,这会是多么的不方便。

对初学者来说最佳实践就是去不断的实验练习,而函数式风格对此有很大的促进作用。

8. 全面的标准库,以及大量可用的免费第三方库

大多数的介绍性编程课程都会让初学者编写一些数学相关的程序,如计算菲波那切数列或者求素数,写一个四则运算计算器,求一个集合的平均数,因为这些都很容易实现而且不需要外部依赖(毕竟,数学运算符在大多数编程语言中是原始运算,因为它们在大多数机器语言中都是原始运算)。

我个人认为如果学生脑海里有现实生活中应用的需求,那么编程将变得更加有趣。例如 用脚本管理自己的电子照片,给他们喜欢的聊天客户端写拓展,互联网图片爬虫等。这些应用会使用到大量的库,幸运的是,Python 中有很多这样的库。没有这些库的帮助,学生永远也不能跳出编写玩具程序的范畴,更别提去创造能激励他们进一步学习的有用的软件了。

9. 合适程度的运算符重载

运算符重载可以让代码看起来更简洁易读,但是滥用也会使代码变得一团糟。对一个编程语言来说允许运算符重载的程度(以及标准库中哪些运算符实际重载),就决定了代码的可读性。一种极端是,在 CJava 中,几乎没有操作符被重载。所以为了实现大部分的操作会调用繁多的函数/方法。另一种极端是,Perl 中大部分操作符都被重载了,在不同的上下文中有不同的含义,这同样使得代码看起来一团糟。我个人觉得 Python 在运算符重载方面取得了一个很好的平衡,比如 +[]

举例来讲,+ 操作符是演示优雅的运算符重载的绝佳例子:数值加法,字符串,列表和元组的拼接等。如果你问初学者他们期望 + 为上述类型做什么,他们很容易猜出正确的功能。另一方面,如果你问初学者他们期望 + 对两个字典做些什么,他们就会感到困惑了,因为现实中没有特别直观的对两个字典进行合并的场景。或许你能将两个字典合并成一个更大的字典,但是当两个字点都包含某几个 key 时,保留哪一个呢?字典中 + 缺乏明确的含义,这可能是为什么它在这种类型上不会过载的原因。

原文:Why Python is a great language for teaching beginners in introductory programming classes
Author: Philip Guo
Translator: 郭瑞彪 校外课

更多关于编程语言的介绍,请参考:
少儿编程之编程语言选择:Python 与 C++ 详细比较
少儿编程之编程语言介绍:Python 编程应用情况详细介绍
少儿编程之学习方法介绍:Python 最佳学习方法 通过八个步骤学习 Python 编程
少儿编程之编程语言选择:Java 与 JavaScript 详细比较
少儿编程之编程语言介绍:HTML 与 HTML5 的区别,HTML5 相比 HTML 有哪些大的改进
少儿编程之学习方法介绍:JavaScript 最佳学习方法 学习 JavaScript 编程的 10 个提示

了解更多少儿编程相关科普文章,请参考:
少儿编程之计算思维的新定义:计算思维是我们想要最小化的摩擦,除非它是生成的
少儿编程之编程思想介绍 ——  思想,作为一种技术
少儿编程之 Kill Math 项目介绍:让数学不只是符号
少儿编程之 Dynamicland 项目介绍:可视空间与设计工坊
少儿编程之二进制:计算机二进制的历史,二进制与十进制的转换及相关亲子互动游戏
少儿编程之随机数:计算机里的随机数,真随机数与伪随机数

了解更多校外课课程介绍,请参考:
新用户指南 - 校外课网站编程学习环境及少儿编程课程介绍
Adafruit 可穿戴编程开发主板 - 用户使用指南
离线学习少儿编程,一些不使用电脑也可以学习编程知识的亲子互动游戏


扫码下面二维码,关注我们的公众号,阅读最新文章或观看更多视频教程:

校外课少儿编程 公众号