Python 和 Go:逐步解析

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 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 中的简洁表达相比,比如列表或字典推导式:

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 解释器在底层所做的事情:

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  和端点到业务逻辑和后端作业处理。

相关文章
|
3天前
|
Python
关于 Python 列表解析式的作用域问题
关于 Python 列表解析式的作用域问题
29 11
|
2天前
|
数据可视化 数据挖掘 API
Python中的数据可视化利器:Matplotlib与Seaborn对比解析
在Python数据科学领域,数据可视化是一个重要环节。它不仅帮助我们理解数据,更能够让我们洞察数据背后的故事。本文将深入探讨两种广泛使用的数据可视化库——Matplotlib与Seaborn,通过对比它们的特点、优劣势以及适用场景,为读者提供一个清晰的选择指南。无论是初学者还是有经验的开发者,都能从中找到有价值的信息,提升自己的数据可视化技能。
|
6天前
|
数据可视化 Python
Python绘制基频曲线——实例解析与应用探讨
Python绘制基频曲线——实例解析与应用探讨
30 9
|
3天前
|
Rust Python
Python 解析 toml 配置文件
Python 解析 toml 配置文件
9 1
|
6天前
|
XML 数据格式 Python
python 解析xml遇到xml.etree.ElementTree.ParseError: not well-formed (invalid token): |4-8
python 解析xml遇到xml.etree.ElementTree.ParseError: not well-formed (invalid token): |4-8
|
3天前
|
Python
Python 解析 yaml 配置文件
Python 解析 yaml 配置文件
10 0
|
3天前
|
Python
Python 解析 ini 配置文件
Python 解析 ini 配置文件
11 0
|
2月前
|
监控 网络协议 Java
Tomcat源码解析】整体架构组成及核心组件
Tomcat,原名Catalina,是一款优雅轻盈的Web服务器,自4.x版本起扩展了JSP、EL等功能,超越了单纯的Servlet容器范畴。Servlet是Sun公司为Java编程Web应用制定的规范,Tomcat作为Servlet容器,负责构建Request与Response对象,并执行业务逻辑。
Tomcat源码解析】整体架构组成及核心组件
|
2月前
|
存储 NoSQL Redis
redis 6源码解析之 object
redis 6源码解析之 object
56 6
|
19天前
|
存储 缓存 Java
什么是线程池?从底层源码入手,深度解析线程池的工作原理
本文从底层源码入手,深度解析ThreadPoolExecutor底层源码,包括其核心字段、内部类和重要方法,另外对Executors工具类下的四种自带线程池源码进行解释。 阅读本文后,可以对线程池的工作原理、七大参数、生命周期、拒绝策略等内容拥有更深入的认识。
什么是线程池?从底层源码入手,深度解析线程池的工作原理
下一篇
无影云桌面