翻译|朱宪忠 审稿人|钱山 这篇文章将向您介绍解释为什么在开发大型项目时不推荐使用Python。 Python没有你想的那么好! 在开发人员的职业生涯中,有一个特定的阶段,从为项目做出贡献到发明自己的技术。有的人这个阶段来得早,有的人可能来得晚,有的人根本就没有这个阶段。大多数职业生涯较长的开发人员都会经历这个,我称之为“自己构建”阶段。 如果你是一名职业生涯较长的开发人员,你应该知道本节标题的真正含义:它是如何工作的?用户体验如何?什么是应用程序框架?数据如何流动?这样的问题还有很多。 我不会在这里为你回答所有这些问题。对于您正在启动的任何项目,它们都是非常具体的问题。这些问题中的每一个都值得至少一篇文章来解释。 尽管如此,我还是很乐意回答一个问题:项目开发哪种语言最好? 你可能会认为这也是一个非常具体的问题,因项目而异。的确,你并没有完全错。但是每种编程语言都有一些缺陷。事实证明,Python也有很多陷阱。特别是当您尝试使用它来构建大型程序时。 变量未声明引起的问题 PythonZen(Python之禅)的忠告是:显式优于隐式。然而,在实际的Python开发中,隐式声明比显式声明更常见。 相比之下,请考虑以下一小段C代码: charnotpython[50]="Thisisn'tPython."; 在我们返回Python之前,让我们深入了解一下以上C代码。 这里,char是一个类型标识符,告诉你后面的一切都是关于字符串的。“notpython”是我给这个字符串起的名字。[50]告诉你C会为此预留50个字符的内存空间。不过,在这种情况下,我得到19个字符-每个空格位置一个字符,最后加上一个空字符\0。最后,用分号简洁地结束它。 这种显式声明在C语言中是必须的。如果省略显式声明,编译器会抛出相应的错误信息!这种做事方式起初可能看起来愚蠢乏味,但它非常值得。 当你在两周或两年后阅读C代码时,例如你偶然发现一个你不知道存在的变量,你只需检查它的声明。当然,如果你给它起一个有意义的名字,那么这会给你一个强有力的线索,让你知道它是什么、它做什么、它需要在哪里等等。 现在,让我们将它与Python进行比较。 在Python中,您几乎可以在编写代码时声明新变量。但是,如果你不给它起一个有意义的名字或者至少留个注释,那么当你后面阅读这段代码时,你会发现问题已经变得一团糟了。 在Python中,除了深入研究有问题的源代码之外,您无法理解变量在做什么。 但是如果你在变量中有错别字,那么它可能会破坏你的整个代码,因为没有像C中那样的守卫声明。 如果你正在做一个较小的项目,比如几千行代码,没关系;或者,如果您的项目不是很复杂,那就有问题了。但是当你有一个更大的项目时,你会遇到很大的麻烦。 是的,您可以在Python中进行显式变量声明。但在现实中,只有最勤奋的程序员才能做到这一点。更现实的是:当编译器不“抱怨”时,许多人只是忘记了那些额外的代码行。 写Python代码快,读Python容易小而简单的项目。 然而,当涉及到阅读和维护大型Python项目时,例如在寻找描述性变量名和注释所有代码时,您最好是世界级的程序员,否则您就完蛋了。 模块归属问题 如果你认为事情不会变得更糟,那你就错了。 变量“存在”在代码中的哪个位置的问题不仅仅源于隐式声明。 变量也可能来自其他模块。它们通常具有my_module.my_variable()的形式。如果您对这样的变量感到困惑,那么当您检查它在主文件中的其他位置时,您还没有完全理解它。 您还需要检查一行代码是否存在,如下所示:您需要导入更多Which函数或变量才能进入多内容模块。 这很烦人,因为您可以在PyPI(https://pypi.org/)上找到更多模块,并且可以在您的计算机上导入任何其他Python文件。因此,快速搜索函数或变量并不总是有效。 但在某些情况下,问题可能会变得更糟。例如,某些模块可能依赖于其他模块。如果你不走运,假设你导入了模块A、B和C,但它们依赖于模块E、F、G和H,而这些模块又依赖于I、J和K。突然间,你不再是三个模块需要管理,你有十个! 更糟糕的是,有时候这个依赖并不是一个简单的依赖树。假设B和C也依赖M和N,J也依赖M,C和H也依赖Q……不用强调,你应该都明白了。 这就像一个迷宫。Python程序员称之为“依赖地狱”——这是真实存在的。 而循环依赖是迷宫中“最丑的野兽”。例如,如果模块A依赖于模块B,但模块B也使用模块A的部分-你就有大麻烦了! 对于小型项目,这没什么大不了的。但是对于大项目……你可能会一头扎进“原始丛林”。 大量的依赖冲突 我还没说完关于模块的吐槽:不仅是模块本身,还有它们的版本。 原则上,Python拥有如此活跃的用户群并且许多模块定期更新是很棒的。然而,只有一个问题:并非所有版本的模块都与其他模块兼容。 例如,假设你正在使用模块A和B。这两个模块都依赖于模块C。但是A需要3.2或更高版本的C,B需要2.9或更低版本的C。你不关心C,你只想要A和B。 世界上没有任何工具可以帮助你解决这个冲突。如果幸运的话,您会找到与您有相同问题的人编写的补丁。如果你不那么幸运,你将不得不自己编写补丁。 或使用不同的包装。或者完全重写包A或B中的一个,并在需要错误版本C的所有地方找到解决方法。 总之,无论如何,您需要花费额外的时间。 这是一个“代码丛林”,您需要耐心和一些工具来驾驭它。 除了依赖冲突,还有一些不错的工具。例如,有一个名为pip的工具可以轻松安装软件包。使用一个简单的文本文件“requirements.txt”,您可以指定要使用的包和版本,而不用弄乱文件头。虚拟环境将所有包保存在一个地方,与主要的Python安装分开。 对于更大更混乱的项目,也可以使用conda、YAML文件等来解决以上问题。 但无论如何,你需要学习如何使用每个工具。而且,您需要花费最少的时间来处理它们。 不同机器上的Python问题 与前面提到的“依赖地狱”世界相关是另一个令人不安的话题。 即使你在自己的机器上解决了所有的依赖关系,并且Python像初生马一样顺畅运行,也不能保证它在别人的机器上也能正常运行。 初生马会跑吗?我不知道,但我似乎正努力变得比以往任何时候都更了解生物学。事不宜迟,让我们回到Python。 pip、文本文件requirements.txt和虚拟环境等工具将在一定程度上帮助您克服依赖地狱。然而,这种便利仅限于当地情况。 在每台新机器上,您将需要检查并可能重新安装每个要求及其版本。 唯一真正便携的解决方案是使用Jupyter笔记本。在这里你可以写任何你喜欢的版本。在Jupyter中,一切都通过在线服务器运行,因此您可以将这些文件发送给任何人,而且它们确实“开箱即用”。 不过,这种方法也有一个明显的缺点:Jupyternotebooks只有图形界面,有时仅使用图形界面很难处理相互关联的文件很多的大型项目。 也许这就是为什么我从未在Jupyternotebooks上看到大型项目,尽管它们确实存在。 相比之下,其他一些计算机语言只使用虚拟机,这样的问题很容易解决。 Pip以外的工具 假设您已经使用Jython或PyPy或类似的解决方案将您的项目移植到不同的机器上。所有这些操作都比虚拟机略显笨拙。但至少他们在工作! 如果你正在组装一个大项目,你可能正在集成C包、Fortran包等。这样做有很多好处:C包在Python中可能不存在,而且通常速度更快。由于历史原因,科学计算包通常只存在于Fortran中。 实际上,您将不得不使用gcc、gfortran等编译器,也许还有更多。 这样太麻烦了!有关在Python代码中集成C模块的文档超过4500字——是本文的两倍!Fortran相关文档也不那么短。 用C构建整个项目最初可能需要更长的时间;但是,您可以避免必须处理多个编译器和接口的情况。 C是一种非常古老的语言,几乎所有东西都有一个C包装器,甚至是用户友好的机器学习包。 使用全局解释器锁锁定性能 全局解释器锁(GIL)从Python诞生的第一天起就已经存在,使最终用户的内存管理变得非常简单。 至少在较小的项目中,开发人员在使用Python时根本不需要考虑计算机内存问题。和C语言比较一下:C语言中,需要为每个变量预留一些内存。 基本上,GIL能够自动计算变量在代码的每个部分中被引用了多少次。如果不再需要该变量,GIL将释放其占用的内存空间。 在小型项目中,GIL可以帮助提升性能,因为不必要的内存空间会被及时清除。 但是在更大的项目中有一个问题:GIL不喜欢多线程! 这是一种在多个指令线程独立运行在同一个进程资源上时,程序高性能执行的方法。机器学习模型非常适合这样的训练。 然而,只有一个小问题:GIL一次只能在一个线程上工作。 所以,就会出现这样一种情况:比如变量A在线程1上执行,而线程2已经用完了变量A,那么它的内存最终可能会被删除。这种情况取决于当时GIL运行在什么地方。 所以这会导致非常奇怪的错误,正如您想象的那样...... 有一些解决方法,但没有一个是漂亮的。另一种选择是通过多个进程来完成计算。但是在没有GIL的语言中,它通常没有多线程那么快。 并发和并行计算仍然令人头疼 我们已经看到并发的缺点之一。多线程时,全局解释器锁会减慢速度,或导致奇怪的错误。 同样的缺点也存在于Python的协程中。 线程和协程之间有一些细微的区别,但最重要的是:协程一次执行一个任务,而线程可以同时执行多个任务,两者都是并发实现的。 当您有需要大量等待的任务时,例如当您从网站读取数据并等待服务器响应时,协程很有用。协程不会让计算机什么都不做,而是给它分配另一个任务。 另一方面,当您有多个耗时但不需要太多CPU消耗且不需要太多等待的任务时,线程很有用。流数据就是一个例子。 如果你有一个CPU密集型任务,并且你想充分利用你的硬件,你可能想尝试使用并行计算。 Multiprocessing将成为您最好的朋友-它基本上告诉计算机使用多个内核进行计算,从而节省大量时间。 Threading、coroutines、multiprocessing,然而,这三种技术都面临着相似的问题。它们在Python中并不难实现。但代码看起来笨重且难以阅读,尤其是对于初学者而言。 Clojure、Go、Haskell等语言在并发和并行方面的表现要好得多。但是,如果您处理的不是缓慢或密集的流程,那么您根本不必考虑这样的解决方案。但是如果你遇到这样的问题,那么你可能需要考虑语言的选择了。 使用哪种语言代替Python? Python并不全是坏东西,但它确实有缺陷。 所以如果你想要文档完善的变量和开发完善的包,不让你轻易陷入依赖地狱,那么你可以选择C语言。此外,如果您想要可移植到任何机器的东西,那么Java、Clojure或Scala都是不错的选择。因为它们在虚拟机上运行,??所以您不会遇到与Python相同的问题。 此外,如果您想执行大型、缓慢的任务,您可能想尝试Go或Haskell。虽然一开始它们比Python更难学,但你投入的时间会得到回报。特别是,您始终可以组合多种语言。 Python非常适合快速编写脚本、初稿,甚至是中型项目。我认识的许多开发人员用Python编写初稿和测试运行,然后用C、Go或Clojure重写重要部分。这使代码执行得更快,并且您仍然可以享受Python所提供的优势。 Python在大型项目中并没有被禁止,只是它可能不是唯一使用的语言。您可以像胶水一样将C、Go或Clojure中的代码与Python拼接在一起。另外,如果您已经到了构建自己的阶段,请记住:没有一种语言是完美的! 总之,尽管有缺点,Python真的很酷,而且也很方便。通过使用Python集成其他语言的代码,总能绕过难点,最终解决问题。 译者介绍 朱显忠,51CTO社区编辑,51CTO专家博主,讲师,潍坊某高校计算机教师,自由编程资深人士。早期专注于各种微软技术(编译成三本与ASP.NETAJX和Cocos2d-X相关的技术书籍)。/ESP32/RaspberryPi等物联网开发技术Scala+Hadoop+Spark+Flink等大数据开发技术。 原标题:Don'tusePython...ifyou'restartingabigproject,作者:AriJoury 链接:https://thenextweb.com/news/dont-use-python-for-大项目
