每年年初,TIOBE 都会公布其年度编程语言。当其最新的年度 TIOBE 指数 报告发布时,我一点也不惊讶地看到 Python 再次赢得桂冠,这主要是基于在 2018 年捕捉到最多的搜索引擎排名积分(尤其是在 Google、Bing、Yahoo、Wikipedia、Amazon、YouTube 和 Baidu 上)。

为了增加 TIOBE 调查结果的分量,今年早些时候,近 90,000 名开发者参与了 Stack Overflow 的年度 开发者调查,这是全球范围内规模最大、最全面的程序员调查。今年结果的主要结论是
“Python,增长最快的主要编程语言,在我们调查的编程语言排名中再次上升,今年超越了 Java,成为第二受欢迎的语言(仅次于 Rust)。”
自从我开始编程和探索不同的语言以来,我一直看到人们对 Python 的喜爱度不断飙升。自 2003 年以来,它一直位居最受欢迎的编程语言前 10 名。正如 TIOBE 的报告所述
“它是当今大学里最常教授的第一门语言,在统计领域排名第一,在人工智能编程领域排名第一,在脚本编写领域排名第一,在编写系统测试方面也排名第一。除此之外,Python 在 Web 编程和科学计算方面也处于领先地位(仅举几个领域)。总而言之,Python 无处不在。”
Python 在 Web 开发、科学计算、测试、数据科学、机器学习等多个领域迅速崛起、蓬勃发展和占据主导地位的原因有很多。原因包括其可读且可维护的代码;对第三方集成和库的广泛支持;模块化、动态和可移植的结构;灵活的编程;易于学习和支持;用户友好的数据结构;生产力和速度;以及最重要的是,社区支持。Python 多样化的应用是其综合特性的结果,这使其优于其他语言。
但在我看来,与其他许多语言的开发者相比,其语法的相对简单性和它提供的惊人灵活性才是胜出的关键。很少有语言能够像 Python 一样适应开发者的编码风格,而不是强迫他或她以特定的方式编码。Python 让更高级的开发者可以使用他们认为最适合解决特定问题的风格。
在使用 Python 时,你就像一个耍蛇人。这让你能够充分利用 Python 的承诺,为开发者提供一个非传统的环境,让他们以最适合特定情况的风格进行编码,并使代码更具可读性、可测试性和连贯性。
Python 编程范式
Python 支持四种主要的 编程范式:命令式、函数式、过程式和面向对象。无论你是否认同它们是有效的甚至是实用的,Python 都力求使所有四种范式都可用且可工作。在我们深入探讨哪种编程范式最适合特定用例之前,现在是快速回顾它们的好时机。
命令式编程范式
命令式编程范式 使用自然语言的祈使语气来表达指令。它以逐步的方式执行命令,就像一系列口头命令一样。遵循“如何解决”的方法,它直接改变程序的状态;因此它也被称为有状态编程模型。使用命令式编程范式,你可以快速编写非常简单但优雅的代码,并且对于涉及数据操作的任务非常方便。由于其相对较慢和顺序的执行策略,它不能用于复杂或并行计算。

考虑以下示例任务,其目标是获取字符列表并将其连接以形成字符串。以命令式编程风格执行此操作的一种方法类似于
>>> sample_characters = ['p','y','t','h','o','n']
>>> sample_string = ''
>>> sample_string
''
>>> sample_string = sample_string + sample_characters[0]
>>> sample_string
'p'
>>> sample_string = sample_string + sample_characters[1]
>>> sample_string
'py'
>>> sample_string = sample_string + sample_characters[2]
>>> sample_string
'pyt'
>>> sample_string = sample_string + sample_characters[3]
>>> sample_string
'pyth'
>>> sample_string = sample_string + sample_characters[4]
>>> sample_string
'pytho'
>>> sample_string = sample_string + sample_characters[5]
>>> sample_string
'python'
>>>
在这里,变量 sample_string 也像程序的状态一样,在执行一系列命令后会发生变化,并且可以轻松提取它以跟踪程序的进度。使用 for 循环(也认为是命令式编程)也可以在上述代码的较短版本中完成相同的操作
>>> sample_characters = ['p','y','t','h','o','n']
>>> sample_string = ''
>>> sample_string
>>> for c in sample_characters:
... sample_string = sample_string + c
... print(sample_string)
...
p
py
pyt
pyth
pytho
python
>>>
函数式编程范式
函数式编程范式 将程序计算视为基于 lambda 演算 的数学函数的求值。Lambda 演算是数理逻辑中的一种形式系统,用于基于函数抽象和使用变量绑定和替换的应用来表达计算。它遵循“解决什么”的方法——也就是说,它表达逻辑而不描述其控制流——因此它也被归类为声明式编程模型。
函数式编程范式提倡无状态函数,但重要的是要注意,Python 函数式编程的实现偏离了标准实现。Python 被称为不纯函数式语言,因为如果你不小心,可能会保持状态并产生副作用。也就是说,函数式编程对于并行处理非常方便,并且对于需要递归和并发执行的任务非常高效。
>>> sample_characters = ['p','y','t','h','o','n']
>>> import functools
>>> sample_string = functools.reduce(lambda s,c: s + c, sample_characters)
>>> sample_string
'python'
>>>
使用相同的示例,将字符列表连接以形成字符串的函数式方法与上述相同。由于计算在一行中发生,因此没有明确的方法可以通过 sample_string 获取程序的状态并跟踪进度。此示例的函数式编程实现非常引人入胜,因为它减少了代码行数,并且仅用一行代码即可完成其工作,但需要使用 functools 模块和 reduce 方法。三个关键字——functools、reduce 和 lambda——定义如下
- functools 是用于高阶函数的模块,它提供对其他函数进行操作或返回其他函数的函数。它鼓励编写可重用代码,因为它更容易复制现有函数,并在已传递某些参数的情况下以文档完善的方式创建函数的新版本。
- reduce 是一种方法,它将具有两个参数的函数从左到右累积地应用于序列中的项,以将序列减少为单个值。例如
>>> sample_list = [1,2,3,4,5] >>> import functools >>> sum = functools.reduce(lambda x,y: x + y, sample_list) >>> sum 15 >>> ((((1+2)+3)+4)+5) 15 >>>
- lambda 函数是小的、匿名的(即无名的)函数,可以接受任意数量的参数,但只输出一个值。当它们用作另一个函数的参数或驻留在另一个函数内部时,它们很有用;因此它们旨在仅在一次实例中使用。
过程式编程范式
过程式编程范式 是命令式编程的一个子类型,其中语句被结构化为过程(也称为子例程或函数)。程序组合更像是一个过程调用,程序可能位于宇宙中的某个位置,并且执行是顺序的,因此成为资源利用的瓶颈。与命令式编程范式一样,过程式编程遵循有状态模型。过程式编程范式有助于良好的程序设计实践,并允许以代码库的形式重用模块。
这种模块化开发形式是一种非常古老的开发风格。程序中的不同模块可能彼此没有关系,并且可以位于不同的位置,但是拥有大量模块会给许多开发者带来困难,因为它不仅会导致逻辑重复,而且还会导致在查找和进行正确调用方面产生大量开销。请注意,在以下实现中,方法 stringify 可以定义在宇宙中的任何位置,并且为了发挥作用,只需要使用所需的参数进行正确的调用即可。
>>> def stringify(characters):
... string = ''
... for c in characters:
... string = string + c
... return stringify
...
>>> sample_characters = ['p','y','t','h','o','n']
>>> stringify(sample_characters)
'python'
>>>
面向对象编程范式
面向对象编程范式 将基本实体视为对象,其实例可以同时包含数据和修改该数据的相应方法。面向对象设计的不同原则有助于代码重用性、数据隐藏等,但它是一个复杂的野兽,并且以面向对象的方法编写相同的逻辑很棘手。例如
>>> class StringOps:
... def __init__(self, characters):
... self.characters = characters
... def stringify(self):
... self.string = ''.join(self.characters)
...
>>> sample_characters = ['p','y','t','h','o','n']
>>> sample_string = StringOps(sample_characters)
>>> sample_string.stringify()
>>> sample_string.string
'python'
>>>
我应该选择哪种编程范式?
重要的是要注意,不同类型的编程范式之间没有比较。由于软件不过是知识表示,因此问题的答案是:“表示我的问题的最佳方式是什么?” 就是选择特定的编程范式。
用外行的话来说,如果你的问题涉及一系列简单的顺序操作,那么遵循老式的命令式编程范式在时间和精力方面将是最经济的,并且会给你带来最佳结果。如果问题需要对值进行数学转换、过滤信息、映射和归约,那么将程序计算视为数学函数的函数式编程可能会派上用场。如果问题被构建为一组相互关联的对象,这些对象具有某些属性,这些属性会随着时间的推移而变化,具体取决于某些条件,那么面向对象编程将非常有用。当然,基于规则的方法在这里不起作用,因为编程范式的选择也很大程度上取决于要处理的数据类型、系统的动态需求以及可伸缩性等各种其他因素。
近期趋势
分析最新的技术流行语可以帮助确定为什么某些编程范式比其他范式更好。
- 机器学习 使用命令式编程和函数式编程的健康组合,并带有一点不变性。特征提取和预处理最好以函数式方式进行,因为它们需要对数据进行数学处理,因为映射、归约和过滤几乎可以并行完成,而无需过多依赖彼此的数据点。机器学习模型的训练最好通过老式的命令式编程来完成,因为优化函数的值(又名程序的状态)需要在每次迭代时更新,因此在算法的许多点都需要顺序执行。在这种情况下,它比函数式编程更快。它还避免了在每一步之后创建所有内容的副本;而是只更新先前的值占位符。
- 深度学习 可以以函数式方式很好地执行,因为深度学习模型是组合式的。整个过程优化了一组复合函数,权重是不可变的和无状态的,并且只要计算出相应的输入,就可以按任何顺序应用更新。使用函数式编程可以免费提供并发性和并行性,并且还可以更轻松地处理大型分布式模型。还有一些自定义范式,其中函数式编程与信息论交织在一起,以避免统计模型中的过拟合。
- 数据操作 可以通过函数式编程或面向对象编程来完成。在函数式编程方式中,一切都是不可变的,算法表达简洁,并且存在原生模式匹配,但数学表达式式命令的制定是一门艺术。以面向对象编程方式进行处理可以提供递归和迭代循环以及基于类的结构,从而更容易扩展以适应更大的数据和新功能。缺点是算法和代码逻辑没有以可读的方式表达。虽然这两种范式都倾向于具有自动垃圾回收系统并且可以平滑地访问和操作数据库,但选择哪一种范式很大程度上取决于程序员的知识。
要点
很有可能任何两位开发者都会在任何情况下最佳编码风格上存在分歧,并且有充分的理由支持他们的观点。关于 Python 的惊人之处在于,它允许你选择在给定情况下最适合你的编程范式。
正如上面的示例所展示的那样,任务始终可以分解为子任务,其中每个较小的部分都以完全不同的范式编码。只要使用的包最少、输入和输出定义明确且复杂度适中,混合搭配风格就能完美运行。没有任何规则说你不能根据需要组合样式。Python 不会在解释应用程序的过程中停止并在你混合样式时显示样式错误。
由于没有完美的指南来为给定的用例选择正确的编码风格,因此最好的建议是尝试几种范式,权衡它们的优缺点,直到你找到一种简单而有效的解决方案。在此实验过程中,你有时会发现,与其在整个过程中使用单一风格,不如使用编程范式的组合来更好地解决解决方案的不同部分。在此过程中,还强烈建议记录需求和不同风格的试验,以便与社区分享并获得反馈。评论和建议将有助于开发以及你的队友和添加到团队中的任何未来的开发者。
Jigyasa Grover 在 10 月 13 日至 15 日在北卡罗来纳州罗利举行的 All Things Open 大会上介绍了 驯服 Python 编程风格。
2 条评论