Python 默认的运行模型是过程型的:我们从主模块的顶部开始然后一句一句执行。所有的 Phthon 对于其他数据和计算模型的方法支持都是基于它是过程型的这一特性。
C 语言毫无疑问仍然是底层编程语言的统治者. 它是实现 Python 解释器的核心语言,同样也是实现 Linux 操作系统内核的核心语言。作为软件开发人员,学习 C 语言是学习更多关于软件所运行的底层硬件的最好起点 – C 语言经常被描述为“可移植的汇编语言”,通常使用 C 语言编译器作为交叉编译器,为新的 CPU 体系结构编译出第一个应用程序。
Rust,相比之下, 是一个由 Mozilla 创建的比较新的语言。它能够进入这个名单的原因是,Rust 吸取了工业界已知的关于不能在 C 语言中做什么的教训,并且被设计成可以与 C 库互操作的语言,它对硬件的控制达到了和低级系统编程语言相同的精度,但它使用不同的编译时方法进行数据建模和内存管理,在结构上消除了许多常见的困扰 C 程序的的缺陷(比如缓存溢出、重复释放内存错误、空指针访问以及线程同步问题)。我是一名嵌入式系统工程师,通过培训具备了最初的专业经验,我已经看到,当前被 C 语言和定制汇编代码统治的各个领域很有可能会被 Rust 取代。
Cython 也是一种默认的更底层的语言,但是与通用目标语言 C,Rust 不同,Cython 主要用于书写 CPython 扩展模块。Cython 被设计作为一个 Python 的超集,让程序员选择何时支持纯 Python 语法的灵活性,当 Cython 语法支持的扩展使其可以生成的代码相当于本地C代码的速度和内存效率。
学习这些语言之一是以实用的角度增强其对内存管理,算法效率,二进制接口兼容性,软件可移植性,将源代码转换成运行系统的深刻理解。
编程中最主要的任务之一是为现实世界的状态建模,这方面最通常的方法是面向对象语言所提供的那些原生的语法支持:把数据结构、操作这些数据结构的方法组合成类。
Python原生设计上就可以直接使用面向对象的特性,而不需要一上来先学习如何编写自己的类。不是每种语言都才有这样的方式 – 对于本节列出的这些语言,学习面向对象的设计思想是使用这些语言的前提。
得益于 Sun Microsystems 在 20 世纪 90 年代中后期对 Java 语言的市场推广,Java 成了很多大专院校计算机科学入门课程的默认语言。虽然现在在很多教育领域它正被 Python 淘汰,但它在商业应用程序开发领域仍然是一种最受欢迎的语言。有一系列其它语言针对公共的 JVM(Java 虚拟机)运行时的实现,包括 Python 的 Jython 实现。Android 系统的 Dalvik 和 ART 环境是基于 Java 编程 API 实现的。
C# 在很多方面与 Java 相似,在 Sun 和 Microsoft 解决关于 J++(微软实现的 Java 语言)和标准 JAVA 不一致的问题失败后,它是作为J++的替代语言出现的。像 Java 一样,它也是一个受欢迎的商业应用开发语言,有一系列其它语言针对共享 .NET CLR(公共语言运行库)的实现,包括 Python 的 IronPython 实现(原始的 IronPython 1.0 中的核心组件被抽取出来创建 .NET 动态语言运行库的中间层)。在很长的一段时间里,.NET是只能在 Windows 系统中使用的专有技术,有一个跨平台的开源代码 mono 重新实现了.NET,但在 2015 年初,微软宣布了 .NET 开源计划。
与列表里大多数语言不同,我并不推荐在日常使用 Eiffel。它之所以在推荐列表里,是因为这门语言有着大量优良的面向对象设计思想,包括以“正确可信”作为程序的设计目标。(同时,Eiffel 也告诉我对于大多数的软件开发,并没有以“正确可信”为设计目标,这是因为正确可信的软件确实无法妥善处理不确定的情况。当很多相关约束还不清楚,需要在不断迭代的过程中去逐步完善的时候,这种设计理念就完全不适合了)
学习这类编程语言,就需要去熟悉继承模型、契约设计、类不变项、前置条件、后置条件、协变(covariance)、逆变(contravariance)、方法查找路径、泛型编程,以及其他各种在 Python 的类型系统上也支持的特性。此外还有很多标准库模块和第三方框架,会用到“显示的面向对象”设计风格,例如 unittest 和 loggingmodules,以及 Django 框架里基于类的 view。
使用 CPython 的一种方式,是把它的内核当做一种“包含对象的 C 语言”的编程环境 – CPython 是通过 C 语言的风格去实现面向对象编程,也就是用 C 的结构体描述数据,然后把结构的实例指针作为第一个参数传给那些数据处理函数(也就是 CPython的C 里面的 omnipresentPyObject* 指针)。这种设计模式被有意复制到 Python 里,在实例方法和类方法需要显示的指定 self 或者 cls 参数。
C++ 的目标是在源码级完全兼容 C 语言,在此之上增加了一些高级特性,例如原生的面向对象编程支持和基于模板的元程序开发。C++ 的晦涩和复杂是臭名昭著的(即使 2011 年的语言标准的更新解决了大量最糟糕的问题),不过就算这样,C++ 仍然是很多场景下的选择,包括 3D 建模图形引擎和跨平台应用开发框架,如 Qt。
D 编程语言也很有趣,因为它和 C++ 的关系很像 Rust 与 C 语言之间的关系:设计 D 语言的目的是,既要保留 C++ 的大部分优点,也要避免 C++ 中存在的很多缺陷(比如缺乏存储安全性)。与 Rust 不同,D 语言不是一个从零开始设计的全新编程语言,相反,它是直接从 C++ 衍生出来的语言,尽管它不像 C++ 那样是 C 语言的严格超集,但它遵守一个设计原则,任何落入 C 语言和 D 语言公共子集中的代码,在两种语言中的行为必须相同。
学习这些语言有利于深刻理解把高级语言特征和底层 C 运行时模型相结合的复杂性。学习 C++ 也有助于使用 Python 操作已有的用 C++ 编写的库和工具包。
面向数组的编程用于数值编程模型:基于矩阵代数和相关的数值方法。
虽然 Python 的标准库并没有直接支持,不过在语言设计上已经做了考虑,一系列语法和语义上的功能支持,有助于第三方库 NumPy 以及类似的面向数组的工具。
在很多情况下,Python科学计算 软件系列都被当做专用的 MATLAB 编程环境的替代者,被广泛用于科学和工程上的建模、仿真和数值分析。开源项目 GNU Octave 的目标是在语法上与MATLAB代码兼容,让人可以比较面向对象编程的这两种方式。
Julia 是另一个相对较新的语言, 它的主要特点是支持面向数组编程和基于类型的函数重载.
学习一种这样的语言有助于深入理解 Python 科学计算工具包的威力,同时,学习这样的语言有助于研究如何利用 OpenCL 和 Nvidia 的 CUDA 等类似的技术实现硬件级并发执行,也有助于研究如何使用 Apache Spark 和 Blaze 等数据处理框架实现分布式数据处理。
由于有越来越多的大数据集需要处理。因此需要一种免费的能处理这样的数据集的分析工具,编程语言 R 就是一种这样的工具,它特别注重统计数据分析和可视化。
学习 R 语言有助于深入理解 Python 科学计算工具包的统计功能,特别是其中的数据分析库 pandas 和统计可视化库 seaborn 。
面向对象数据建模和面向数组数据建模主要用于对数据进行静态建模,有两种建模方式,一种是把数据保存在对象的各个属性中,另一种是把结构化的数据保存为数组。
相比之下,函数式编程语言更强调以计算流的形式对数据进行动态建模。只要学一下函数式编程基础,就会显著提高使用数据转换操作对数据建模的能力,这对于使用其他范式的编程语言(比如面向过程、面向对象、面向数组的编程语言)开发应用程序也是有帮助的。
Haskell 是一个函数式编程语言,对 Python 的设计产生过重大的影响, 最明显的就是 Python 2.0 引入的列表解析。
Scala 毫无疑问是基于JVM的函数式编程语言,与Java, Python和R一样,是Apache Spark数据分析平台的四门主要编程语言之一。在设计上支持函数式编程方式的同时,Scala的语法、数据模型和执行模型在设计上尽量避免为原有的Java程序员带了太大的障碍(从这个角度上看,Scala更恰当的分类应该是有着强函数式语言支持的面向对象的编程语言)。
Clojure是另一门基于JVM的函数式编程语言,被看作是Lisp的一个变种。它在我们的清单中具有一席之地,是因为它为Python的函数式编程工具箱toolz的实现带来灵感。
我自己对F# 并不熟悉,不过由于它是 .NET CLR 推荐的语言,所以还是值得关注的。
学习这些编程语言,有助于了解 Python 自身的计算管道建模工具,包括容器推导表达式、生成器、生成器表达式、functools 和 itertools 标准库模块,和第三方函数式 Python 工具如 toolz。
计算管道是处理数据转换和分析问题的一种很好的方法,不过很多问题需要程序以持久的方式运行,等待事件发生,然后处理这些事件。对这类服务,通常可以并发的处理多个事件,来实现同时为多个用户(或者至少多个行为)提供服务。
JavaScript 最初是为浏览器开发的事件处理编程语言,可以让 web 开发人员处理客户端本地的用户行为(例如鼠标移动和按键)和事件(例如页面渲染结束)。所有现代的浏览器都支持 JavaScript,与 HTML5 的 DOM 一起,已经成为用户界面的外观和行为事实上的标准。
Go 是Google设计出来的,设计这个语言的目的是为了创建高度可扩展的网络服务,Go语言非常适合开发命令行程序。从设计编程语言的角度看,最引人注目的是Go语言在它的核心并发模型中使用了“顺序通信过程(Communicating Sequential Processes)”这一概念。
Erlang 是爱立信设计出来的,设计这个语言的目的是为了制造高度可靠的电话交换机以及类似的设备,著名的开源框架RabbitMQ的消息服务器就是用Erlang实现的。Erlang使用Actor模型实现了核心并发原语,不允许不同线程直接共享数据,线程间的通信只能靠传递消息。尽管我自己从来没有使用过Erlang语言, 但我的第一份工作涉及到了一个基于Actor模型开发的并发框架,它是一个前爱立信工程师用C++开发的,我自己也基于TSK(任务)和MBX(邮箱)原语开发过这样的框架,是在德州仪器的轻量级DSP/BIOS运行时(现在叫TI-TROS)中实现的。
Elixir 能够出现在这个名单中的理由是,虽然它运行在Erlang虚拟机中,与编程语言Erlang具有相同的并发语义,但它也包含了一系列额外的语言级特征,提供了更全面的开发环境,更容易吸引从其他编程语言(比如Python、Java或Ruby)转过来的开发者。
学习一种这样的语言有助于深入理解Python本身是如何支持并发和并行的,包括原生协程、基于生成器的协程、concurrent.futures和asyncio标准库模块、第三方网络服务开发框架(比如Twisted和Tornado)、Django中新引入的channels概念和GUI 框架中的事件处理循环。
Python 3.5 引入的特性里,最有争议的一项是新的类型模块,为 Python 体系加上了混合类型的支持。
对于那些接触过的静态类型编程语言主要是 C, C++ 和 Java 的开发者来说,这简直就是一个及其可怕的想法。
Microsoft 的 TypeScript 为 JavaScript 应用提供动静混合类型支持,让你对这个概念会有好一些的看法。TypeScript 代码会编译成 JavaScript 代码(编译后不包含任何运行时类型检查),主流的JavaScript 库的 TypeScript 注释(annotations)在DefinitelyTyped代码库里可以找到。
正如 Chris Neugebauer 在澳大利亚 PyCon 大会的报告上指出的那样,这很像是 Python 与类型提示库 typeshed 以及类似 mypy 那样的类型推导和分析工具之间的关系。
本质上,TypeScript 和 Python 的类型提示都是实现特定测试程序的方式,不管是独立文件(常规的测试程序),还是内嵌在主代码里(类似静态编程语言的类型声明)。不管哪种情况,你都可以运行单独的命令检查剩余的代码是否符合已知的类型约束(对于 JavaScript 和 TypeScript,在编译阶段会隐式的去完成,对于 Python 的类型提示,则是可选的静态分析任务)。
像 C、C++、C#、Java 这样的编程语言给 Python 带来的一个有点让人不安特性是“代码即数据”:类似函数和类都是运行时对象,可以被其他对象操作。
Hy 是一个 Lisp 的变种,可以在 CPython 虚拟机和 PyPy 虚拟机上运行。Lisp 在“代码即数据”上做到了极致,Lisp 代码本身就是由描述需要实现的操作的嵌套的列表组成的(这门语言的名字就源自”LISt Processor”)。Lisp 风格的语言,最强大的一点是它们可以很轻松的实现自己的领域特定语言(DSL),不过这有时候也为阅读其他人的代码带来困难。
Ruby 在很多方面都与 Python 很类似,但是作为更为开放的社区,Ruby 更接受动态元程序设计,而对于 Python,这方面只是“支持,但不鼓励”。这方面的功能包括重定义类加入一些方法,用闭包实现语言核心结构如迭代器。
学习这些语言可以帮助深入了解 Python 自身的动态元程序设计的支持,包括函数和类装饰、monkeypatching(动态修改代码)、unittest.mock 标准库模块以及第三方对象代理模块入如wrapt(我还没找到什么编程语言有助于了解 Python 的元类(metaclass),如果有人有好的建议,可以在评论里告诉我。元类的高级特性包括核心类型、抽象基类、枚举类型和混合类型(动态类型和静态类型混合)表达式的运行时执行)。
流行的编程语言通常并不是孤立的 —— 他们属于庞大的生态系统的一部分(商业和社区都是这样),此外还有终端用户、框架开发者、工具开发者、教育人员等等。
Lua是一门流行的编程语言,主要作为脚本引擎内嵌于大型的程序里。值得一提的例子有,为魔兽争霸游戏客户端编写的插件,在很多 Linux 发行版存在的 RPM 工具也内置了 Ruby。与 CPython 相比,Lua 运行时大小只有其十分之一,并且它的弱反省(weaker introspection)的能力也能让它更容易独立于应用程序的其他部分和宿主操作系统。一个值得提到的来自 Lua 社区对 Python 生态系统的贡献是,CPython 和 PyPy 采用 LuaJit FFI(Foreign Function Interface)作为其 JIT 友好的 cffi 接口库的基础。
PHP 是另一个受欢迎的编程语言,由于PHP擅长生成HTML页面,被早期的虚拟服务器主机提供商广泛使用,因此它作为 LAMP stack(Linux-Apache-MySQL-PHP)的组成部分被广为人知。尽管PHP在设计中存在很多令人苦恼的概念上的缺陷,它仍然成了很多著名的开源web 服务的基础,包括Drupal内容管理系统、WordPress博客引擎和支撑Wikipedia的MediaWiki引擎。PHP也能支撑很多重要的服务,比如众包社区所使用的分布式事件报告平台 Ushahidi。
和PHP一样,Perl 也是Linux系统上的一个受欢迎的语言,与PHP不同,Perl不是作为网站开发平台被人熟知的,它更常见的用途是作为系统管理员管理系统的工具,它既能使用正则表达式处理字符串又能处理基于文本的Linux操作系统命令的输出结果。只使用Perl就能处理所有的任务,不需要再使用Whensh、awk和sed等工具了。
学习其中的一门语言并不能提供任何好的见解在审美上漂亮或者在理念上简洁的程序语言设计。可能的结果是在实践中提供一些编程语言的结构和采纳的知识,以及了解关于偶然的机会、历史的积累和降低入门门槛(通过重新分配使缺省变得可能)所起到的作用,这些都强于语言本身固有的能力。
数值计算的思想:Scratch,Logo
最后想说的是,我常常陷入这样的讨论,即结构化编程和面向对象倡导者的争论。后者自称面向对象编程语言和结构化编程语言一样易学。
当我们谈论的是通过具体的数值实验来教学(机器人学),研究对象在仿真软件中的模型有着直接现实世界的参照物时,比如学生们可以接触到传感器,发动机,继电器等。我认为支持面向对象的小伙伴们有一定的道理。
然而对于其他人来讲,我遇到的一个典型的挑战是:拿起一本食谱,将其中一个菜谱转换成你认为易学的面向对象编程语言,然后找到一个理解这门编程语言的学生,沿着我的思路,来继续转换这个菜谱。(我期待着看到学术研究人员真正践行这样的学习过程,——我会发自内心的为这样的情况感到欣慰。)大多数的情况下,小伙伴们不必遵循这样的流程——仅仅需要在头脑中进行思维的实验就足以让他们感受到要想学会这“易学的”编程语言需要多少预备知识。
然而另外一个解决此问题的方法是学习那些用于教育小孩子数值计算的编程语言。
其中一种最流行的莫过于 Scrach,它是一种让学生利用拖动的方式来操作封闭的图形化环境,从而可以看到图形化界面中相应的移动和反应的编程接口。像 Scrach 这样的图形化环境是一种类似于利用连环画帮助孩子们逐步学习读书认字的方式的程序设计方式。
然而,这种利用一种特殊教育目的编程语言来操作一个图形化界面的想法并不新奇,随着的早期最经典环境之一的 Logo 环境在 2 0世纪 60 年代的创建(类似于 Python 自己的海龟模块),那时候,你所接触的主要的东西是一个“海龟”,你可以用命令它的移动来画线,从而改变图形环境。通过这种方式,像命令行、迭代、状态(例如:向上划、向下划)都以一种建立在人们的自然直观的思维方式(想象一下,假如你是一只海龟,如果向右旋转 90 度将会发生什么?)的基础上来介绍。
回归本源,作为一名富有经验的程序员,重新学习以上的任何一门编程语言是最有效的方式来忘掉所学(抛弃一些轮子):这些语言工具所涵盖的概念帮助我们回想起那些我们曾经认为理所当然的概念,但是需要以初学者的眼光重新学习。当我们这么做的时候,因为我们更加愿意回想起整个的逻辑链条,包括那些我们之前认为理所当然而省略的思维步骤,我们会更加有效地和学生以及其他的初学者一起工作。
作为一个开发者,有一个学习的氛围跟一个交流圈子特别重要,这是一个我的Python交流群:795225532,不管你是小白还是大牛欢迎入驻,分享Python零基础入门全套资料、面试经验,讨论技术, 大家一起交流学习成长!希望帮助开发者少走弯路。