当前位置:   article > 正文

Python is Easy. Go is Simple. Simple != Easy

Python is Easy. Go is Simple. Simple != Easy

简介

Python 和 Go 具有独特的品质,可以相辅相成。

alt

有一个常见的误解,认为“简单”和“容易”指的是同一件事。毕竟,如果某样东西易于使用,那么它的内部工作原理也一定很容易理解,对吧?或者相反也是一样?事实上,情况恰恰相反。虽然这两个概念指向相同的结果,但要让事情看起来简单,背后需要巨大的复杂性。

alt

以Python为例,这种语言因其低入门门槛而闻名,因此成为编程入门的首选语言。全球各地的学校、大学、研究中心和大量企业都选择了Python,因为无论人们的教育水平或学术背景(或完全没有)如何,任何人都可以使用它。

人们很少需要太多类型理论或了解事物如何及其在内存中的存储位置、运行某些代码的线程等等。此外,Python是通往一些最深奥的科学和系统级库的入门门户。能够用一行代码控制如此强大的功能,在很大程度上有助于它成为最流行的编程语言之一。

但问题来了——用Python代码表达事物的便捷性是有代价的。在底层,Python解释器非常庞大,即使是一行代码也必须执行许多操作才能运行。当您听到有人将Python称为“慢”语言时,大部分感知到的“慢”来自解释器在运行时做出的决策数量。

但在我看来,这还不是最大的问题。Python运行时生态系统的复杂性,加上围绕其包管理的一些自由设计决策,导致环境非常脆弱,更新通常会导致不兼容和运行时崩溃。让Python应用程序在几个月后返回到它,却发现主机环境已经发生了足够的变化,甚至不再可能启动该应用程序,这种情况并不罕见。

当然,这过于简单化了问题,甚至现在的孩子都知道容器的存在就是为了解决这样的问题。事实上,借助Docker及其类似工具,我们可以及时“冻结”Python代码库的依赖项,使其实际上可以永远运行。然而,这是以将责任和复杂性转移到操作系统基础设施为代价的。

从轻松到简单

如果我们使用Python来解决这些问题,我们最终会得到类似于Rust的东西——性能极高,但入门门槛也非常高。

在我看来,Rust不易使用,也不简单。尽管它现在完全被炒作了,我虽然拥有20年的编程经验,并且已经在C和C++中迈出了第一步,但我无法在看到一段Rust代码后就确信我理解了其中发生的事情。

alt

大约五年前,当我在开发基于Python的系统时,我发现了Go。虽然我尝试了几次才开始喜欢这种语法,但我立刻就爱上了这种简单的理念。Go的目标是让组织中的任何人都能简单地理解——从刚从学校毕业的初级开发者到偶尔查看代码的高级工程经理。

更重要的是,作为一种简单的语言,Go很少进行语法更新——最后一个重要的更新是在v1.18中添加泛型,这是经过十年认真讨论的结果。在大多数情况下,无论您是查看五天前还是五年前编写的Go代码,它基本上都是相同的,并且应该可以正常工作。

然而,简单需要纪律。一开始可能会感觉受限,甚至有些落后。特别是与Python中的简洁表达式相比,例如列表或字典理解:

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
}

  • 1

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

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
    }
}

  • 1

虽然 Python 能够编写出功能等同的代码,但编程领域内一条不言而喻的原则是,如果某种语言提供了更为简洁(无论是在简洁性还是优雅性方面)的解决方案,程序员往往会偏好使用该语言。然而,简洁性是主观的,并且理应对每个程序员都具有相同的适用性。可供选择的执行相同操作的替代方法促成了多样化的编程风格,而在同一代码库中发现多种风格的情形并不罕见。

Go 语言因其冗长和“乏味”的特性,自然而然地满足了另一项需求——Go 编译器在编译可执行文件时所需完成的工作量远远小于其他语言。编译并运行 Go 应用程序的速度通常与启动 Python 解释器或 Java 虚拟机一样迅捷,甚至更快。毫不夸张地说,Go 程序的启动速度几乎与本地可执行文件一样快。尽管其速度不及 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和端点到业务逻辑和后端作业处理。

到目前为止,这种共生关系运行良好,我希望未来也能继续保持。

本文由 mdnice 多平台发布

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/很楠不爱3/article/detail/317855
推荐阅读
相关标签
  

闽ICP备14008679号