摘要:本文作者是Python的设计者之一Nick Coghlan,总结了提升Python的27种编程语言,包括过程式编程、面向对象的数据模型、面向对象的C语言衍生等。
【编者按】本文作者是Python的设计者之一 Nick Coghlan,总结了提升Python的27种编程语言。
以下为正文:
作为全球最流行的编程语言联合设计者之一,我经常看到的一种令人沮丧的行为(在Python社区和其它社区都有),就是那些有影响力的人试图把“缺失”的恐惧感灌输给其它开源社区,将其当作对社区贡献的源动力。(我偶尔会对自己的这种不当行为感到内疚,当别人落入同样的陷阱时我也就更容易察觉到)。
虽然借鉴其他编程语言社区的经验是一件好事,但以恐惧为基础的方法来激励行动存在严重的问题,因为它将助涨社区成员为争取贡献者的关注而将其它社区的成员视为敌人,而不是当做潜在的盟友去迎接更大的挑战,共同推动顶尖软件技术的进步。这也相当于宣告不欢迎那些青睐其它编程语言的人们来到这个社区,这是一个把他们和他们的同伴视为“恶敌”的社区。
事实上,我们希望有丰富多样的跨平台开源编程语言可供选择,因为程序设计语言是最重要的思维工具——它们首先让我们能够明确地传递表达自己的想法,以至于即使是计算机也能理解。如果有人发现一种编程语言既符合他们的思路,又能解决当下的问题,真是太棒了,选了哪种(哪几种)编程语言显得无关紧要。
因此,我对Python社区有三点具体的要求,以及一条更泛化的建议。首先,是这三点具体要求:
- 当我们觉得有必要用部落的本性来激发动力时,我们应该避免使用部落的恐惧,而是力求引起部落自豪感。若把恐惧作为动力,如这样措辞“如果不解决X问题,我们将会流失一部分开发者,他们就会转向使用Y编程语言”,那么我们就是刻意地在那些向大众无私分享代码的贡献者之间传播负能量。如果是利用部落自豪感,措辞则会是“现在完全不清楚如何用Python解决X问题。如果我们浏览Y语言的生态圈,会发现他们有一种完美的方法可以解决X问题,我们也能借鉴此方法用Python提供同样完美的用户体验”。积极赞扬肯定了我们自己的努力成果,而非诋毁别人的工作,它能够促进Python社区内持续学习的文化,同时也鼓励改善与其它社区的促进合作关系。
-
克制对其它开源编程语言社区的轻蔑态度,尤其是如果那些社区协助人们自己解决了问题,而不是让人们等着商业软件开发商来解决。世界上许多关键问题的解决过程都无利可图(因为被问题所困扰的个人并不富裕,也无权决定科研经费的去向),因此我们应该鼓励和称赞那些加紧努力去解决问题的人们,不管我们对他们选用的技术有何看法。
-
当我们得知某个人第一次学习编程,而且他们选择了一种我们个人不喜欢的编程语言,我们无论如何都应该支持他们的选择。他们比我们更清楚什么适合他们的思维,所以适合我们的语言并不一定也适合他们。如果他们开始对最初选择的语言感到沮丧,甚至到了打消他们学习编程的积极性的程度,这时才应该给他们推荐其它语言。这个建议也适用于我们这些参与提高糟糕的网络安全状态的人们:我们使用内在不安全的语言来解决问题的方法是通过提高操作系统的沙盒能力,逐步消除障碍,并采用内部安全性更好的语言,提高现有语言的默认行为,而不是与新手争论为什么他们选择的语言从应用安全的角度而言是糟糕的选择,使得他们一头雾水。(如果有人正在使用新手开发的未经审核的软件来处理安全敏感任务,那么问题不是出在软件开发者身上,而是在于使用者对该软件的来源和安全性没有进行尽职的调查。)
我的建议主要针对那些即将遭遇Python核心程序集的限制以及因此打算探索更多Python自身的“思维工具”的人们。
作为Python核心开发的一部分,我们的工作包括观察自己使用过的编程语言,找出其中令人欣赏的特性,看看是否有办法将其引入Python,使Python代码既易读也易写。这也意味着学习另一种专注于特定风格的软件开发编程语言,能够帮助人们在Python环境下更好地理解同样的编程风格。
为了在这方面有所帮助,我在下面罗列了一些可能的探索领域,以及其它一些编程语言也许能对这些领域提供额外的见解。我尽可能地把链接指向维基百科页面,而不是直接指向相关内容的主页面,因为维基百科往往提供一些有趣的历史背景,若是作为学习实践而不是立即进行实际应用时,花时间探索一番背后的历史是值得的。
尽管我个人确实了解这其中的许多编程语言(并且在开发产品过程中已经用过几种),完整的推荐表还是额外添加了一部分我间接了解(往往是通过阅读教程和设计文档,或是和我信任的朋友交流这门语言的优势和弱势)的编程语言。
有很多其它编程语言 本应该也出现在这份列表中,然而只是根据我的个人兴趣随机挑选出了一部分(比如,我主要的兴趣点在于Linux、Android和Windows生态系统,所以我放弃了小众但仍有市场的以Apple为中心的Objective-C和Swift语言,而且我也不熟悉如 Processing 之类的art-focused环境,猜不出它们能让Python开发者学到什么)。若不仅仅想知道编程语言教给开发者的有什么想,而是要一份更完整的列表,IEEE Spectrum每年的编程语言 年度热门排行榜 值得你一看。
过程式编程:C, Rust, Python
Python的默认执行模式是过程式的:我们从主模块的顶部开始,依次执行各条命令。下文所述的Python对其它所有数据和计算建模方法的支持都是在过程式基础上搭建的。
C语言仍旧是底层过程式编程无法撼动的标尺。它是Python解释器的核心实现语言,也是Linux操作系统内核的实现语言。作为一名软件开发者,学习C语言是开始深入学习执行软件应用的底层硬件的最好方法之一——C语言常被称为是“可移植的汇编语言”,而且C语言编译器也是首先为任何新CPU结构交叉编译的几款应用之一。
Rust语言 恰好相反,它是Mozilla开发的一种相对较新的编程语言。把它列在此是因为Rust的目标是吸取所有我们从工业界已经得到的关于C语言弊端的教训,设计一种能和C语言库交互操作的新语言,提供底层系统编程语言对硬件相同精确的控制能力,但是它采用一种不同的编译时方法实现数据建模和内存管理,从而结构性地避免了许多C语言的常见缺陷(诸如缓存区溢出,重复释放指针错误,空指针获取和线程同步问题)。我是一名通过实践训练成长起来的嵌入式系统工程师,Rust是我所见过的第一种新语言,它似乎最有可能衍生拓展到所有细枝末节以取代目前所有的C语言和自定义汇编代码。
Cython 也是一种底层的默认过程式语言,但是不像一般的通用语言,如C和Rust,Cython主要是为编写CPython扩展模块而设计。为了支持这一目标,Cython被设计成Python的超集,允许程序员自由选择何时使用纯Python语法来体现代码的灵活性,何时又用Cython的语法扩展来编写代码,使其拥有与传统C语言一致的速度和内存使用效率。
学习上述语言中的一门能够深入理解内存管理、算法效率、二进制接口兼容性、软件便携性,以及如何将源代码转化为能够允许的系统的实践经验。
面向对象的数据模型: Java, C#, Eiffel
在编程的时候,我们做的一件重要事情就是对现实世界的状态建模,目前流行的解决方法之一是提供面向对象编程的本地化语法支持:结构化地组织数据结构,以及将处理这些数据结构的方法封装成类。
Python本身经过巧妙地设计,使得我们不需先学会定义类就能直接使用其面向对象的特性。并不是每种编程语言都支持这一方法——在这一小节列出的编程语言,就需要在使用它们之前先学习面向对象的编程。
随着九十年代中后期Sun Microsystems的大力市场推广, Java 成为许多学校计算机科学专业入门学习的默认语言。尽管现在它的地位在许多教育场所已经被Python所取代,它仍旧是商业应用开发的最流行语言之一。有大量的其它语言是需要在JVM(Java虚拟机)的环境下运行,包括Python的Jython实现。Android系统的Dalvik和ART环境是基于Java编程API的再实现。
C# 和Java在许多方面都相似,当Sun和Microsoft无法理清Microsoft的 J++ 商业化差别而宣告失败时,C#以替代产品的身份面世。与Java一样,C#也是商业应用开发的流行软件之一,也有许多其它语言依附于共享的 .NET CRL (Common Language Runtime),包括Python的IronPython实现(原始的IronPython 1.0 版本的核心部件被提取用于创建语言中立者.NET动态语言运行时)。在很长的一段时间里,.NET和跨平台开源重实现的工具 mono 一样,都是Windows特有的专利技术,但在2015年上半年Microsoft将.NET转向了 开源生态系统的策略 。
与这个列表里的大部分语言不同,我并不推荐把 Eiffel语言 作为日常使用的语言。相反,我推荐它的原因是它的学习过程教会了我无穷无尽的面向对象的优秀设计,它以“正确无误”为应用设计目标。(Eiffel的学习过程也让我明白了为何事实上“正确无误”并不是大多数软件开发的设计目标,因为确保无误的软件真的不能很好处理模棱两可的情况,若是你暂时真的不知道相关约束条件且需要给自己留有足够的回旋余地以便后续迭代开发时,确保无误的软件也完全不适合这种情况)。
学习以上一种语言使你能够深入了解继承模型、合同设计、类不变性、前置条件、后置条件、协方差、逆变性、方法解析顺序、泛型编程,以及其它同样适用于Python类型系统的概念。也有许多标准库和第三方框架使用这种“可见的面向对象”设计风格,如单元测试和日志模块,还有Django Web框架的基于类的视图。
面向对象的C语言衍生:C++, D
使用CPython运行的方式之一是以“C with objects”的编程环境——在其核心,CPython是采用C的面向对象编程来实现的,通过定义C语言的struct来存放数据,而后把struct的实例作为函数的第一个参数传入并处理该数据(这些就是CPython的C语言API中无处不在的PyObject*指针)。这种设计模式是故意在Python层面镜像,将显式的self和cls参数转为实例方法和类方法。
C++语言 旨在保留与C语言的完整兼容性,同时加入更高级别的特性,如对原生面向对象编程的支持和基于模板的元编程。它的冗余和复杂性已是臭名昭著(虽然2011版语言标准更新解决了许多糟糕的问题),但它也是很多环境下的备选开发语言,包括3D建模图像引擎和跨平台应用开发框架Qt。
D语言 也很有趣,它与C++的关系就如同Rust与C的关系:因为D语言的目的是保持C++最理想的特性,同时也避免许多问题(如缺少内存安全)。与Rust不同,D语言不是从零开始设计的一门新语言——而是C++的亲密衍生物,并且它并不像C++那样是C语言的一个超集,它却又遵循设计原则,任何属于C和D语言公共子集的代码必须在两种语言中有相同的行为方式。
学习以上一种语言能够洞察将高级语言的特性与底层C语言运行模型融合的复杂之处。学习C++对于用Python操作现有C++编写的库和工具也很有帮助。
面向数组的数据处理: MATLAB/Octave, Julia
面向数组编程的设计目标是支持数值编程模型:那些基于矩阵的运算和其它相关的数值计算。
尽管Python的标准库并不直接支持这项功能,面向数组的编程确实是在语言设计之时被纳入考虑的,增加了大量的语法和语义特点,使得第三方库 Numpy 和类似的面向数组工具受益颇多。
在很多情况下, Scientific Python 被当做是MATLAB商业软件的备选项,后者被广泛用于科学和工程上的建模、仿真以及数值数据分析。 GNU Octave 是语法上兼容Matlab代码的开源替代产品,它允许人们比较和对比两种面向数组的编程方式。
Julia语言 是另一款相对较新的语言,它重点专注于面向数组的编程和基于类型的函数重载。
学习上述语言中的一门能够对Scientific Python的功能有充分的掌握,提供了探索硬件层面并行计算的机会(如借助OpenCLojure和Nvidia的CUDA等技术)和分布式数据处理的的能力(如利用 Apache Spark 和为Python定制的 Blaze 等技术)。
统计数据分析:R
随着访问大数据集需求的增长,对分析这些大数据的免费工具的需求也随之增长。其中一种工具就是 R语言 ,它主要针对数据的统计分析和可视化。
学习R语言就像是深入了解Scientific Python的统计分析能力,尤其是数据处理函数库 pandas 和统计可视化库 seaborn 。
计算管道模型:Haskell、Scala、Clojure、F#
面向对象的数据模型和面向数组的数据处理着眼于在静态环境下构建数据模型,或是以带有标签属性数据集合的形式,或是包含结构化数据数组的形式。
相反,函数式编程语言强调动态地构建数据模型,通常是以计算流的形式。学习函数式编程的基础对数据转换操作的结构改善大有裨益,即使是应用在过程式、面向对象或是面向数组的应用上也有效。
Haskell 语言是一款函数式编程语言,它对Python的设计有着显著的影响,最著名的就是来自Python 2.0中关于 list comprehensions 的介绍。
Scala 语言是一款JVM上的函数式(有争议)编程语言,与Java、Python和R一起,是Apache Spark数据分析平台上所使用的四种主要编程语言。尽管是按照鼓励用函数式编程方式来设计,Scala的语法、数据模型、执行模型的设计都是为了尽可能减小现在的Java程序员的学习成本(因此也就存在争议—— Scala更应该算是一种有强大函数式编程支持的面向对象编程语言)。
Clojure 是另一款JVM上的函数式编程语言,它算是 Lisp 的一种变种。它能够在此列表中赢得一席之地归因于它是Python的 toolz 函数式编程工具箱的灵感来源。
F# 语言 我自己并不熟悉,但作为 .NET CRL推崇的函数式编程语言似乎也值得提一笔。
学习这些语言中的一门能够深入了解Python自身的计算管道模型工具,包括容器,生成器,生成器表达式,以及functools和itertools的标准库模块,和 toolz 等第三方Python工具包。
事件驱动型编程:Javascript, Go, Erlang, Elixir
计算管道是处理数据转换和分析问题的一种出色方法,但很多问题需要应用能提供持续的服务,以等待事件的发生,然后处理那些事件。在这类服务中,为了同时对付多个用户(或者至少是多个行为),能同时处理多个事件通常是基本要求。
Javascript 语言期初是为网页浏览器设计的事件处理语言,允许网站开发者能在本地响应客户端的行为(例如鼠标点击和按键)和事件(例如页面渲染完成)。它被所有现代浏览器所支持,并与HTML5 DOM一起,成为定义用户界面展示和行为的标准。
Go 语言 是是Google为网页浏览器设计的专用语言,用来创建高度可扩展化网络服务,它也被证明是开发命令行应用的强大语言。从编程语言设计角度来看,Go语言最吸引人的方面是它在核心同步模型中采用了 通信顺序过程 (Communicating Sequential Processes)的概念。
Erlang 语言是Ericsson设计的专用语言,用于发明高可靠性的电话交换机和类似器件,也是广为使用的消息管理器 RabbitMQ 的开发语言。Erlang使用 参与者模式 (Actor model)作为其核心并发原语,线程间的通信只能靠传递消息,而不允许它们直接共享数据。尽管我自己从未用Erlang语言写过程序,我的第一份全职工作就用到了(以及开发)一个前Ericsson工程师写的C++版Actor-based并发框架,并且基于德州仪器的轻量级 DSP/BIOS 环境(现为TI-RTOS)下的TSK(任务)和MBX(邮箱),我自己也开发了一套类似的框架。
Elixir语言 上榜的缘由是它是一款运行在Erlang虚拟机上的语言,与Erlang一样具有并发的特性,同时也额外提供了丰富的语言层面的特色来营造一个更便捷的环境,吸引更多的开发者从Python、Java或者Ruby等语言转来投奔这里。
学习这些语言中的一门就如洞察Python本身的同步机制和并行化支持,包括原生协程、基于生成器的协程、concurrent.futures和asyncio的标准库模块,如 Twisted 和 Tornado 等第三方网络服务开发框架,被引入Django的 channels 概念,以及GUI框架下的事件处理循环。
渐进式类型:TypeScript
Python 3.5 引入最有争议的特性之一就属新的typing模块,它将渐进式类型支持(gradual typing support)的标准带到了Python生态圈内。
对于那些主要从C、C++和Java等语言接触静态类型的人们而言,这似乎是一个令人震惊的坏想法(因此遭受争议)
Microsoft的 TypeScript 语言为JavaScript提供了可选的静态类型,它是这个概念(gradual typing)更形象的解释。TypeScript代码编译为JavaScript代码(随后不再做运行时类型检查),并且TypeScript对于流行的JavaScript库的注释都保存在专属的 DefinitelyTyped 代码库中。
正如Chris Neugebauer在他的 澳洲PyCon演讲 中指出的那样,这很像是Python 与类型提示库 typeshed 以及类似 mypy 那样的类型推导和分析工具之间的关系。
从本质上看,TypeScript和Python中的类型提示都属于编写特定测试用例的方法,不论是写成独立的文件(常规测试用例),还是嵌入代码的主题之中(如静态编程语言的类型声明)。无论何种情况,你都运行一个单独的命令来检查剩余的代码是否和类型声明保持一致(对于TypeScript,这隐式地作为JavaScript编译的一个部分,对于Python的类型提示,这则是可选的静态分析任务)。
动态源程序:Hy, Ruby
从C、C++、C#和Java之类语言转来的人们往往会对Python的一种特性感到不安,就是“代码即数据”:事实上,函数和类都是运行时对象,能像其它对象一样被操作。
Hy语言 是Lisp的一种变种,运行在CPython虚拟机和PyPy虚拟机上。Lisp的变种语言将“代码即数据”的概念发挥到了极致,因为Lisp代码自身也是由描述要执行操作的嵌套列表组成(其名字就源自”LISt Processor”)。List风格语言的强大之处在于它们使你写自己的领域特定语言(DSL)变得异常容易。而最大的弱点在于由于写自己的领域特定语言变得非常容易,有时候阅读别人的代码将会非常吃力。
Ruby语言 是一门与Python在许多方面都相似的语言,它的社区在使用动态元编程方面更加开放,相对而言Python社区则是“支持,但不鼓励”。这包括重新定义类添加额外的方法,以及使用闭包实现类似迭代的核心语言结构。
学习以上一门语言将对Python自身的动态元编程支持有更深的理解,包括函数和类的修饰,拼凑代码来修改逻辑( monkeypatching ),unittest.mock标准库模块和诸如 wrapt 的第三方对象代理模块。(我不清楚学习哪一门语言有助于了解Python的元类(metaclass)系统,若是有人在这方面有任何建议,欢迎在评论区留言。元类支持的特性包括核心类型、抽象基类、枚举类型和渐进式表达式的运行时执行。)
实际问题解决:Lua, PHP, Perl
普遍流行的编程语言并不是独立地存在——它们属于更大生态系统的一部分,其中包括再分配者(redistributors,商业和社区都有)、终端用户、框架开发者、工具开发者、教育工作者等等。
Lua语言 是一门流行的编程语言,常用做脚本引擎嵌入在大型应用中。典型的例子包括它是编写魔兽争霸游戏客户端插件的语言,被嵌入到很多Linux发行版所使用的RPM工具中。相比起CPython,Lua的运行时大小只有十分之一,而且它的弱内省(introspection)能力通常使它更易于与应用的剩余部分和主机操作系统隔离。Lua社区对Python生态圈的一项突出贡献就是CPython和PyPy采用LuaJit FFI (Foreign Function Interface)作为其JIT友好的 cffi 接口库的基础。
PHP语言 是另一门流行的编程语言,由于它专攻生成HTML页面,且被早期的虚拟专用服务器托管服务提供商广泛支持,其首字母“P”是 LAMP stack(Linux-Apache-MySQL-PHP)中的“P”继而广为人知。因为其在设计方面诸多令人绝望的概念性缺陷,它被视作是目前多款开源web服务的基石,包括内容管理系统Drupal,博客引擎Wordpress, 以及为维基百科服务的MediaWiki引擎。PHP也支撑许多重要服务,比如众包社区(crowdsourced community)所使用的分布式事件报告平台 Ushahidi 。
与PHP一样, Perl语言 也是在Linux的基础上流行开来的。但和PHP不同的是,Perl并没有发展成网络开发的专用平台,而是因系统管理员工具而出名,不仅能使用正则表达式处理字符串,还能处理Linux操作系统命令的输出内容。当sh、awk和sed已经无法再胜任时,Perl则担负起重任。
学习上述一门语言似乎并不有助于洞察美学上美观、概念上简练的程序语言设计。可能的帮助在于理解编程语言如何在实际工作中被使用,以及使用再分布器又会有有多少偶然机会、历史巧合的成分,而不是语言本身继承而来的能力。
尤其是,它让我们明白一些项目的意义,如 CKAN 、 OpenStack NFV 、 Blender 、 SciPy 、 OpenMDAO 、 PyGMO 、 PyCUDA 、 Raspberry Pi Foundation 和 大量商业组织的Python项目 ,后者确保Python生态圈能获得持续的慈善投资。
计算式思维:Scratch, Logo
最后,我经常会参与函数式编程和面向对象编程主张者的讨论,他们声称这些语言和过程式编程一样容易学习。
当我们谈论通过具体实例计算(如机器人)的教学时,我赞同面向对象编程的人所持的观点,因为软件中建模的对象在实际生活中有学生们能感触到的对应模块,如传感器、电机和继电器。
对于其它人而言,我现在有一项挑战:拿起一本食谱,用你认为易学的编程语言翻译其中一个菜谱,然后让一名能掌握原语言的学生照着翻译后的菜谱做。大多数时候,人们并不真的需要按照上述去做——只需要在大脑中假象一次思维实验就足以让他们意识到想掌握这“易学”的语言需要多少预备知识。(我真心期待学术研究员真的能进行这个实验——并且发自内心地想得到答案。)
然而解决这个问题的另一种途径是去学习真正用来教育孩子们计算式思维的语言。
其中一种最流行的莫过于 Scratch 语言,它提供拖拽的编程接口让学生们操作一个独立的图形化环境,其中有精灵来回漂浮,并响应学生们的操作。诸如Scratch的图形化环境类似于我们帮助孩子们阅读和书写的连环画册。
使用定制的教学语言来操作图形化环境的想法并不算新颖,早在1960年代 Logo 环境就是当时早期的经典之一。
在Logo里(以及类似的环境比如Python的turtle模块),你主要的交互对象就是一只“海龟”,可以让它来回移动或是画线条改变它的环境。诸如命令序列(command sequence)、重复(repetition)和状态(state,如收起画笔、放下画笔)的概念就以一种建立在人们自然直观的思维方式被引入。(“设想你就是那只海龟,如果你向右转90度将会发生什么呢?”)
作为一名经验丰富的程序员,回过头再重新学习上述一门编程语言则是获取新感想的最有效方式:这些语言所涵盖的概念提醒着我们,这都是些我们现在看来理所当然的概念,但是初学者却需要在某个阶段学习它们。当我们这么做的时候,我们与学生以及其他的初学者的沟通合作将会更加有效率,因为我们更容易回想起整个逻辑链条,包括那些我们之前认为理所当然而省略的思维步骤。 |