社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Python

不要使用Python开发大型项目!

Python编程 • 3 年前 • 605 次点击  

本文经授权转自公众号CSDN(ID:CSDNnews)

整理 | 张仕影  责编 | 郑丽媛
在很大程度上,Python并不像你想象的那么好。

在开发者的职业生涯中,有一个特定阶段,开发者们会从为项目做贡献到掌握自己的技巧。这个阶段对有些人来说会来得早一些,有些则也会晚一些,而另一部分人则无法到达这个阶段。

不过,大多数职业生涯较长的开发人员都经历过这个阶段。我将这称之为自己构建的点。

如果你已经到达了那个阶段,你面对的第一个问题是:它是如何运作的?用户体验如何?架构是怎样的?数据如何流动?以及很多类似这样的问题。

我在这里就不为你回答这些这些问题的答案了。无论你开始哪一个项目,它们都需要根据项目来高度定制,并且每一个都应该至少有一篇独立的文章来解答。

不过,我想回答其中一个问题:哪种语言最适合这个项目?

你可能认为这取决于项目的类型,的确,但是每种编程语言都有一些缺陷。实际上,Python 也有很多缺陷,尤其是当你试图用它来构建一个大型程序的时候。

1、变量声明不存在,这是一个问题

Python 之禅表明:显式优于隐式。但当涉及到变量声明时,Python 中的隐式比显式更常见。(注:Python 之禅指的是 Tim Peters 编写的关于 python 编程准则。)

我们先来看看下面这一小段的 C 代码:

char notpython[50] = "This isn't Python.";

在对比 Python 之前,我们先来深入研究以下这个问题。

“char” 是一个类型标识符,表示之后的所有内容都与一个字符串有关。“notpython” 是我给这个字符串取的名字。[50] 表示C将为此保留50个字符的内存空间。不过,在本例中,我可以使用 19个 字符——每个字符一个,在末尾加上一个空字符\0,最后用一个分号巧妙地结束。

这种显式声明在 C 语言中是强制性的。如果你忽略它,编译器则会罢工。

这种方式起初看起来既愚蠢又乏味,但是它的回报非常大。

两周或两年后读 C 代码的你突然发现了一个你不知道的变量,那么查一下声明就可以。如果你给它起了一个有意义的名字,那这就会给你一个极大的提示:它是什么,它在做什么,以及哪里需要它。

对比一下 Python,你几乎可以随时创造变量。但如果你没有给它取一个有意义的名字,或者至少留下一个关于它的注释,那么将来可能会一团糟。

在 Python 中,除了深入研究代码,否则很难理解变量在做什么。甚至如果你在变量中有一个拼写错误,你可能会破坏整个代码——Python 并没有像 C 语言那样的保护声明。

如果你要处理的是较小的项目,或者不是很复杂的项目,比如说几千行代码,这就没有什么问题 ,但如果是更大的项目…那就糟糕了。

你可以在 Python 中做显式变量声明,但只有一些非常勤奋的程序员才会这么做。当编译器没有问题时,很多程序员们往往会忘记这些额外的代码行。

编写 Python 是很快的,对于一些小而简单的项目来说,阅读 Python 也是很快的。不过阅读和维护大型 Python 项目时,你最好成为找描述性变量名和注释所有代码的世界英雄,否则你就完了。

2、模块,你属于哪里?

如果你认为事情不会变得更糟糕,那么你就错了。

变量从哪里开始“存在”于代码中的问题不仅仅来自隐式声明,还可能来自其他模块,它们通常是以 my_module.my_variable() 这样的形式存在。如果你被这样一个变量搞糊涂了,那么当你检查它出现在主文件中的其他位置时,你依旧会感到困惑。

你还必须检查代码中是否包含以下两行代码之一:

import my_modulefrom another_module import my_module

第二行的作用是告诉编译器,你需要从一个包含更多内容的模块中获得某个函数或变量。

这很烦人,因为有模块比你在 PyPI 上找到的更多,还可以在计算机上导入任何其他 Python 文件。所以快速搜索你的函数和变量并非全然有益,甚至它可能会变得更糟糕。

模块可以依赖于其他模块。因此如果你不走运,你导入了模块 A、 B 和 C,但是这些模块依赖于模块 E、 F 、G 和 H,而模块 E、 F、G 和 H 又依赖于模块 I、 J 和 K。突然之间,你需要管理的不仅是 3 个模块,而是 10 个模块。

更糟糕的是,有时候并不是这么简单的结构。比如说模块 B 和 C 也取决于模块 M 和 N ,而 J 也依赖于 M、C 和 H 依赖于 Q…后面不必多说,你懂的。

这是一个迷宫,也是一个由 Pythonians 创造且真实存在的依赖地狱。

循环依赖是迷宫中最丑陋的野兽。如果模块 A 依赖于模块 B,但模块 B 同时使用模块 A 的一部分…

在小项目中这并不是问题,但在大项目中……欢迎来到丛林。

3、大量依赖冲突

我要抱怨的不仅仅是模块本身,还有它们的版本。

原则上, Python 有活跃的用户群,很多模块也会定期更新,这是非常好的。只是有一个问题:并非所有版本的模块都能与其他模块始终保持兼容。

比如说你使用模块 A 和 B,而两者都依赖于模块 C。但是 A 需要3.2或者更高版本的 C, B 需要2.9或者更早的版本 C。

可你不在乎模块 C,你只想要 A 和 B。

世界上没有任何工具可以帮助你解决这场冲突。幸运的话,你会找到一个和你遇到同样问题的人写的补丁。如果你不是那么幸运,你将不得不编写补丁。或者你用别的包。再或者,你可以重写其中一个包(A 或者 B),然后在需要 C 的地方找到变通方法。

无论如何,你都需要额外的时间来解决问题。这就像在一个丛林里,你需要耐心以及一些导航工具来让自己找到出路。

抛开依赖冲突,也有一些不错的工具。像“pip”,它就可以很容易地安装软件包。使用一个简单的“requirements . txt”,你可以指定哪些包以及你希望使用哪些版本等等。虚拟环境将所有包放在一个地方,并与主要 Python 分开安装。

对于更大更复杂的项目,还有“conda”、 YAML 文件等等。但是你需要学习如何使用每一种工具,确保用最少的时间解决问题。

4、不同的机器,不同的 Python

即使你已经解决了机器上的所有依赖问题,你的 Python 运行起来十分流畅,也不能保证它在其他机器上运行时依旧这么流畅。像“pip”、“ requirements . txt ”这样的工具和虚拟环境可以帮助你浏览轻度依赖地狱,但仅限于本地。

在每台新机器上,你都需要检查并重新安装各个版本及要求。

唯一简便的解决方案是 Jupyter notebooks。在 Jupyter notebooks 中你可以用任何你喜欢的版本写东西。在 Jupyter 中,一切都运行在一个在线服务器上,你可以将这些文件发送给任何人,他们同样能够使用它们。

不过这也有一个明显的缺点:Jupyter 笔记本只有图形界面。但是使用图形界面,处理具有许多相互关联文件的大型项目是相当困难的。

也许这就是为什么我很难在 Jupyter 笔记本上看到大项目的原因吧。

而其他语言只要有虚拟机,问题就解决了。

5、pip 之外的世界

假设你已经设法通过使用 Jython 或 PyPy 或其他解决方案,将你的项目移植到不同的机器上(虽然这些比虚拟机处理起来要笨拙一些,但至少能奏效),接下来为了整合大型项目,你可能会集成 C 包、 Fortran 包等。这样做有许多好处:C包可能不存在于 Python 中,而且通常更快。由于遗留原因,科学包往往只存在于 Fortran 中。

但实际上,在这过程中你必须使用像 ‘gcc’、‘gfortran’ 这样的编译器,甚至其它更多编译器——这很麻烦,因为在 Python 代码中集成 C 模块的文档超过 4500 字,集成 Fortran 的文档也不短。

所以如果一开始就用 C 构建整个项目可能会更好:虽然要慢一些,但是你可以避免必须使用多个编译器和接口的情况。

C 很古老,几乎任何东西都有包,甚至还有用户友好的机器学习软件包。

6、使用全局解释器锁锁定性能

全局解释器锁(GIL),从 Python 诞生的第一天起就已经存在,它使终端用户的存储管理变得非常容易。

至少在较小的项目中,开发人员在使用 Python 时根本不需要考虑计算机内存。相比之下,在C中每个变量都保留了内存位!

基本上,GIL 会计算一个变量在代码中每个部分被引用了多少次。如果不再需要该变量,则会释放它所占用的内存空间。因此在小型项目中,GIL 有助于提高性能,因为不必要的内存空间被清除掉了。但是在大项目中有一个问题:GIL 不喜欢多线程。

这是一种可以极大提高执行程序性能的方式,其中多个指令线程在相同的进程资源上独立运行。机器学习模型非常适合以这种方式进行训练。

只有一个小问题:GIL 一次只能在一个线程上工作。因此,如果变量 A 在线程 1 上执行,而线程 2 已经完成了变量A ,那么它的内存可能会被删除,这取决于当时 GIL 处在什么位置。

正如你想象的那样,这可能会导致非常奇怪的错误。

当然,这是有解决方法的,但它们都不是很完美,因为它通常不会像有 GIL 语言中的多线程那样快。

7、并发和并行仍然笨重和混乱

我们已经发现了并发的一个缺点,就是当你正在进行多线程时,全局解释器锁会减慢速度,或者导致奇怪的错误。

同样的缺点也存在于 Python 的协同程序。

线程和协程有一些细微的区别,即协程一次执行一个任务,而线程可以同时执行多个任务。相同点在于,它们都是并发的实现。

当你有大量需要等待的任务时,协程很有用,比如你正在读取网站数据并等待服务器响应。协程程序并不会让计算机坐视不管,而是将另一个任务分配给它。另一方面,当你有几个耗时的任务时,线程的优势就体现出来了,不太占用 CPU ,也不需要太多等待。

如果你有一个 CPU 密集型任务,并且想要充分利用你的硬件,那么并行是值得考虑的。

多处理也是个不错的选择,它会告诉计算机使用多个核心,节省时间。

不过,线程、协程和多处理这三种技术都面临类似的问题,即在 Python 中实现它们并不难,但是代码看起来很笨拙,很难读懂,尤其是对于初学者。

像 Clojure 、 Go 和 Haskell 这样的语言,在并行性和并发性方面要好得多。如果你处理的不是缓慢或密集型任务,就无需考虑。但如果你是,你可能要重新考虑你的选择。

8、用什么代替Python

Python 自有其不可忽视的优势,但它也的确存在缺点。

如果你想要明确声明的变量和开发良好的包,避免陷入依赖地狱,那么 C 就很不错。

如果你想要的东西是可移植到任何机器的,那么 Java, Clojure 或 Scala 是很好的选择。它们是在虚拟机上运行的,所以你不会遇到像 Python 一样的麻烦。

而如果你想运行大型缓慢的任务,你可能会想试试 Go 或者 Haskell。一开始,它们比 Python 难学,但是你投入的时间是有回报的。

你还可以组合不同的语言,比如 Python 非常适合快速编写脚本、草图,甚至是中等规模的项目。我认识的许多开发人员都是用 Python 编写第一稿和测试运行,然后用 C 、Go 或 Clojure 重写重要部分。这可以使代码执行得更快,同时还可以享受 Python 提供的优势。

在大型项目中也并不是不能使用 Python,只是很多情况下它并不是唯一:你可以使用 Python 来拼凑 C 、 Go 或 Clojure 中的各个部分。

如果你已经达到了自己构建的目标,请记住,没有任何一种语言是完美的。尽管 Python 有其缺点,但它依旧很棒,也很方便,你可以通过集成其他语言的代码来避开 Python 这些缺点。

原文地址:https://thenextweb.com/news/dont-use-python-for-big-projects

本文转自公众号“CSDN”,ID:CSDNnews

--- EOF ---


推荐↓↓↓
Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/130422
 
605 次点击