Python 和 Go :简单了解

Python 和 Go 各具特色,能够互补

有一个常见的误解认为 简单 (simple)和 容易(easy)指的是同一件事。毕竟,如果某样东西易于使用,那么其内在机制必须也简单易懂,对吗?或者反之亦然?实际上正好相反。虽然这两个概念精神上指向相同的结果,但让外表看起来容易需要底层极其复杂的设计。

以 Python 为例,这是一种因其入门门槛低而被广泛喜爱的编程语言,因此成为入门编程语言的首选。全球的学校、大学、研究中心以及大量企业选择 Python,正是因为它对任何人都易于接近,无论他们的教育水平或学术背景如何(甚至完全没有)。人们很少需要太多类型理论或理解事物如何及在何处存储在内存中,哪些代码片段在哪个线程上运行等。此外,Python是通向一些最深奥的科学和系统级库的入口。用一行代码控制这么大的能量,足以说明其成为地球上最流行的编程语言之一的优势所在。

但这里存在一个问题 - 用 Python 代码表达事物的便捷性是有代价的。在底层,Python 解释器庞大,即使是执行单行代码也需要进行许多操作。当你听到有人称 Python 为"慢速"语言时,所谓的"慢速"很大程度上来自解释器在运行时做出的决策数量。但在我看来,这甚至不是最大的问题。Python 运行时生态系统的复杂性,加上其包管理周围一些自由的设计决策,构成了一个非常脆弱的环境,更新常会导致不兼容和运行时崩溃。发现一个 Python 应用程序几个月后再回来使用时,仅因宿主环境的足够变化就可能导致应用程序无法启动,这并不罕见。

当然,这是一种严重的简化,甚至现在的孩子都知道容器存在是为了解决这类问题。实际上,多亏了 Docker 及其类似的工具,可以"冻结" Python 代码库的依赖项,使其实际上可以永远运行。但这是以将责任和复杂性转移到操作系统基础结构为代价的。这不是世界末日,但也不是可以轻视和忽略的事情。

从容易到简单

如果我们要解决 Python 的问题,我们可能会得到像 Rust 这样的东西------极其高效但入门门槛显然很高。在我看来, Rust 既不容易使用,也不简单。虽然现在它非常热门,但尽管我有 20 年的编程经验,从 C 和 C++ 开始,我看着一段 Rust 代码也不能肯定地说我明白其中发生了什么。

大约五年前,当我在处理一个基于 Python 的系统时,我发现了 Go。虽然我尝试了几次才开始喜欢它的语法,但我立刻被简单的理念所吸引。Go 旨在让组织中的任何人都能简单理解------从刚毕业的初级开发者到只偶尔看代码的高级工程经理。更重要的是,作为一种简单的语言,Go 很少更新语法------最后一次重大更新是在 v1.18 版本中添加泛型,这是经过十年的严肃讨论之后的事情。大部分情况下,无论你是看五天前还是五年前写的 Go 代码,它们大致相同,并且应该能正常工作。

简单性需要纪律性。起初,它可能感到限制性,甚至有些落后。特别是与 Python 中的简洁表达相比,比如列表或字典推导式:

ini 复制代码
temperatures = [
    {"city": "City1", "temp": 19},
    {"city": "City2", "temp": 22},
    {"city": "City3", "temp": 21},
]

filtered_temps = {
    entry["city"]: entry["temp"] for entry in temperatures if entry["temp"] > 20
}

在 Go 中编写相同的代码需要敲击更多的键盘,但理想情况下应该更接近 Python 解释器在底层所做的事情:

go 复制代码
type CityTemperature struct {
    City      string
    Temp float64
}

// ...

temperatures := []CityTemperature{
    {"City1", 19},
    {"City2", 22},
    {"City3", 21},
}

filteredTemps := make(map[string]float64)
for _, ct := range temperatures {
    if ct.Temp > 20 {
        filteredTemps[ct.City] = ct.Temp
    }
}

虽然你可以用 Python 编写等效代码,但编程中有一个不成文的规则说,如果语言提供了一个 更简单 (即,更简洁、更优雅)的选项,程序员会倾向于使用它。但容易是主观的,简单应该对每个人都同样适用。执行相同操作的替代方法的可用性导致了不同的编程风格,一个代码库中经常可以发现多种风格。

由于 Go 冗长且"无聊",它自然满足了另一个条件 - Go 编译器在编译可执行文件时的工作量要少得多。编译并运行 Go 应用程序通常和启动 Python 解释器或 Java 的虚拟机之前的速度一样快,甚至更快。不出所料,作为本地可执行文件的速度是可以做到的最快的。它不像其 C/C++ 或 Rust 对手那样快,但代码复杂性却少了很多。我愿意忽略 Go 的这个小"缺点"。最后但同样重要的是,Go 二进制文件是静态绑定的,意味着你可以在任何地方构建它,并在目标主机上运行------无需任何运行时或库依赖。为了方便起见,我们仍然会将 Go 应用程序包装在 Docker 容器中。不过,这些容器显著更小,并且它们的内存和 CPU 消耗只是 Python 或 Java 对应物的一小部分。

如何将 Python 和 Go 的优势结合起来使用

我们在工作中发现的最实用的解决方案是结合 Python 的 容易 和 Go 的 简单 。对我们来说,Python 是一个绝佳的原型设计游乐场。这是思想诞生的地方,科学假设得到接受和拒绝的地方。Python 自然适用于数据科学和机器学习,既然我们要处理大量这类工作,尝试用其他东西重新发明轮子就没什么意义。Python 也是 Django 的核心,这体现了它的口号,即允许快速应用程序开发,这一点很少有其他工具(当然,Ruby on Rails 和 Elixir 的 Phoenix 也值得称赞)能够做到。

假设一个项目需要哪怕是最少量的用户管理和内部数据管理(像我们的大多数项目一样)。在这种情况下,我们会从 Django 框架开始,因为它内置的 Admin 非常棒。一旦粗糙的 Django 概念验证开始类似于一个产品,我们就会确定其中有多少可以用 Go 重写。因为 Django 应用程序已经定义了数据库的结构和数据模型的外观,所以编写在其之上的 Go 代码相当容易。经过几次迭代,我们达到了一种共生状态,其中两边在同一个数据库之上和平共存,并使用基本的消息传递彼此通信。最终,Django 的"外壳"成为了一个协调器------它服务于我们的管理目的,并触发接下来由其 Go 对应物处理的任务。Go 部分处理其他一切,从面向前端的 API 和端点到业务逻辑和后端作业处理。

相关推荐
智慧老师1 分钟前
Spring基础分析13-Spring Security框架
java·后端·spring
Kai HVZ27 分钟前
python爬虫----爬取视频实战
爬虫·python·音视频
古希腊掌管学习的神29 分钟前
[LeetCode-Python版]相向双指针——611. 有效三角形的个数
开发语言·python·leetcode
m0_7482448332 分钟前
StarRocks 排查单副本表
大数据·数据库·python
B站计算机毕业设计超人39 分钟前
计算机毕业设计PySpark+Hadoop中国城市交通分析与预测 Python交通预测 Python交通可视化 客流量预测 交通大数据 机器学习 深度学习
大数据·人工智能·爬虫·python·机器学习·课程设计·数据可视化
路人甲ing..42 分钟前
jupyter切换内核方法配置问题总结
chrome·python·jupyter
游客5201 小时前
opencv中的常用的100个API
图像处理·人工智能·python·opencv·计算机视觉
每天都要学信号1 小时前
Python(第一天)
开发语言·python
搬码后生仔1 小时前
asp.net core webapi项目中 在生产环境中 进不去swagger
chrome·后端·asp.net
凡人的AI工具箱1 小时前
每天40分玩转Django:Django国际化
数据库·人工智能·后端·python·django·sqlite