这篇文章的作者有一群极客朋友,他们经常讨论技术话题,有时也会讨论编程语言。“我讨厌Python”,作者说。他对Python有着绝对的厌恶。就算有现成的Python代码,他也宁愿用C重写。为了系统地吐槽Python,作者特地写了这篇博客,详述了Python的“八宗罪”。该话题在HackerNews上引发热议(评论400+),感兴趣的读者可以观看或参与。黑客新闻讨论:https://news.ycombinator.com/item?id=187061741。版本如果你想安装默认的Linux操作系统,那么你很可能需要安装多个版本的Python:Python2、Python3甚至3.5、3.7。原因是:Python3与Python2不完全兼容。甚至一些分数版本(如3.5、3.7)也明显缺乏向后兼容性。我完全支持为编程语言添加新功能,我什至不介意弃用一些旧功能。但是Python必须单独安装。我的Python3.5代码不适用于Python3.7安装,除非我有意将其导入3.7。很多Linux开发者觉得导出太麻烦,所以在安装Ubuntu的时候会把Python2和Python3一起安装——因为有些核心功能需要前者,有些则需要后者。缺乏向后兼容性和孤立的发布通常为它敲响了丧钟。Commodore创造了第一台家用电脑(远早于IBMPC和Apple)。但CommodorePET与后续的CommodoreCBM不兼容。CBM与VIC-20、Commodore-64、Amiga等不兼容。因此,您要么选择花费大量时间将代码从一个平台移植到另一个平台,要么选择放弃该平台。(今天的Commodore在哪里?早就被用户抛弃了。。。)同样的,Perl也流行过一段时间。但是Perl3也与许多Perl2代码不兼容。社区抱怨,所以一些好的代码被导出,其他的被丢弃。Perl4也是如此。当Perl5出来时,人们干脆转向了另一种更稳定的编程语言。今天,只有少数人仍然频繁使用Perl来维护以前的项目。但是没有人再用Perl创建新的大项目了。同样的道理,每个版本的Python也都有谷仓效应。以前的版本还在那里,你最终会得到一堆旧的无用的Python代码,因为没有人愿意花时间将它移动到最新版本。据我所知,没有人再为Python2创建新代码,但我们保留它是因为没有人想将所需代码移到Python3.x中。Python2.7、3.5、3.6和3.7的文档仍在Python官方网站上积极维护,因为他们无法下定决心放弃以前的代码。Python就像一种僵尸编程语言——死去的部分在行尸走肉中还活着。2.安装很多软件包可以帮助你轻松运行apt、yum、rpm或其他一些安装库,并获得最新版本的代码。但Python并非如此。如果你用“apt-getinstallpython”安装,你不知道你安装的是哪个版本,它可能无法兼容你需要的所有代码。因此,您安装所需的Python版本。我的一个项目使用了Python,但是必须使用Python3.5。所以***,我的电脑安装了Python2、Python2.6、Python3和Python3.5。其中两个来自操作系统,一个用于项目,一个用于因其他原因安装的不相关软件。虽然都是Python,但是这个Python不是那个Python。如果你想安装Python包,你应该使用“pip”(PipInstallsPackages)。但是由于您的系统上有一堆Python,因此您需要注意使用正确版本的pip。否则,“pip”可能会根据需要运行“pip2”而不是“pip3.7”。(如果名字不存在,需要给pip3.7指定显式真实路径)队友建议我配置环境,让每个软件都能使用Python3.5的基础环境。在我需要使用Python3.6开始另一个项目之前,这一直很有效,但是对于Python3.6,我必须创建另一个环境。两个项目,两个版本的Python,一点都不混,真的(对生活假笑)。pip安装程序将文件放在用户的本地目录中。不使用pip安装系统级库。Gawd不允许您错误地运行“sudopip”,因为那会毁掉您的整个计算机!运行sudo可能会导致在系统级别安装一些包,一些包用于错误版本的Python,并且你的main目录中的一些文件可能最终归root所有,因此未来非sudopip安装可能会失败并出现权限问题.不要这样做。谁维护这些pip模块?当然是社区。也就是说,没有明确的所有者,也没有强制性的起源链或责任链。今年早些时候,在PyPI的一个版本中发现了一个窃取SSH凭据的后门。这也是意料之中的。(出于同样的原因,我不使用Node.js和npm;我不信任他们的社区项目。)3.语法我大力提倡可读代码。乍一看,Python的可读性似乎不错。但是当您开始创建大型代码库时,您不会这么认为。大多数编程语言使用某种符号来标识范围——函数开始和结束的位置、条件语句中包含的操作、变量定义的范围等等。C、Java、JavaScript、Perl和PHP都使用{...}来定义范围,而Lisp使用(...)。蟒蛇呢?它使用空格!如果你想为复杂的代码定义一个范围,你可以在接下来的几行代码中缩进,当缩进结束时,范围就结束了。Python手册说您可以使用任意数量的空格或制表符来定义范围。但是,每个缩进使用四个空格!如果你想缩进两次嵌套,使用八个空格!Python社区对此进行了标准化,尽管Python手册中并未明确说明。这个社区喜欢使用四个空间。因此,除非您不打算向任何人展示您的代码,否则请为每个缩进使用四个空格。当我第一次看到Python代码时,我认为使用缩进来定义范围会很好,但这样做有一个巨大的缺陷。您可以嵌套很深,但这样做会使每一行变得很长,并且您必须将其换行在文本编辑器中。长函数和条件语句会使匹配开始和结束范围变得困难。当您不小心将三个空格当作四个空格时,您很容易出现计算错误,这可能需要数小时才能进行调试和追踪。对于其他语言,我已经养成了不缩进调试代码的习惯。这样,我可以快速浏览代码,然后轻松识别和删除调试代码。但是Python呢?任何未正确缩进的代码都会产生缩进错误。4.includes大多数编程语言都有导入其他代码块的方法。例如在C语言中可以使用“#include”,在PHP语言中可以使用“include,include_once,require,require”。Python使用“导入”。Python可以导入整个模块、模块的一部分或模块内的特定函数。C语言?您可以检查“/usr/include/”。对于Python,最好用“python-v”列出所有路径,然后从列表中搜索每个目录和子目录中的每个文件。我有一些喜欢Python的朋友,但是当我看到他们要导入一些东西时,我总是不得不浏览标准模块。导入功能还允许用户重命名导入的代码。他们基本上定义了一个自定义命名空间。乍一看,你可能觉得没问题,但这最终会影响可读性和长期支持。重命名适用于较小的脚本,但实际上不适用于长期项目。那些用1-2个字母作为命名空间(比如“importnumpyasn”),不遵守命名规范的,拉出来枪毙!这还不是最糟糕的。大多数编程语言在包含代码时,只是导入代码。如果存在带有构造函数的全局对象,某些语言(如面向对象的C++)可能会执行代码。同样,一些PHP代码可能会定义全局变量,因此导入可以运行代码——但这种做法通常被认为是不好的。相反,许多Python模块包含在导入期间运行的初始化函数。你不知道什么在运行,它在做什么,你甚至可能不会注意到。除非存在命名空间冲突,否则如果这很有趣,您将花费大量时间试图找出原因。5.命名法在其他语言中,数组直接称为'arrays',但在Python中,它们被称为'lists'。关联数组在某些地方称为“散列”(Perl),但Python将其称为“字典”。Python似乎以自己的步调前进,避免使用计算机科学和信息科学领域的通用术语。此外,Python库的命名也存在问题。PyPy、PyPi、NumPy、SciPy、SymPy、PyGtk、Pyglet、PyGame……(前两个库发音相同,但功能完全不同)。我知道“py”是指Python,但它们不能都排在前面或后面吗?一些常见的库已经放弃了类似双关语的“Py”命名约定,包括matplotlib、nose、Pillow和SQLAlchemy。虽然有些名称可能暗示了它们的用途(例如SQLAlchemy包含SQL,因此它可能是一个SQL接口),但其他名称可能只是随机词。如果你不知道“BeautifulSoup”库是干什么的,你能从名字看出它是一个HTML/XML解析器吗?然而,BeautifulSoup文档齐全且易于使用,如果每个Python模块都是这样,我不会抱怨,但大多数Python库的文档都很糟糕。总的来说,我将Python视为具有不一致命名约定的库的集合。我经常抱怨开源项目的糟糕命名。除非您知道这些项目在做什么,否则您将无法从命名本身分辨出任何内容。除非您知道要查找的库是什么,否则您只能通过名称或偶然找到一些库。大多数Python库加剧了这种现象,以及Python的负面体验。6.奇特的操作每种语言都有自己奇特的操作。C中使用&和*来获取地址空间和值的命名法很奇怪。在C语言中,也有++和--实现自增自减的捷径。在Bash语言中,在引用某些字符(如正则表达式中使用的括号和句点)时,总是需要考虑“何时使用转义符(\)”。JavaScript兼容性问题(并非每个浏览器都支持所有有用的功能)。但是Python比我见过的任何其他语言都有更多的怪癖。例如:在C语言中,字符串用双引号括起来,字符用单引号括起来。在PHP和Bash中,两种类型的引号都可以包含字符串。但是,用双引号括起来的字符串可以嵌入到变量中。相比之下,单引号字符串是文字;任何嵌入的类似变量的名称都是不可扩展的。在JavaScript中,单引号和双引号没有区别。在Python中,单引号和双引号没有区别。但是,如果您希望字符串跨行,则必须使用三重引号,例如“”“string”””或'''string'''。如果你想使用二进制,那么你需要更喜欢带有b(b'binary')或r(r'raw')的字符串。有时您想使用str(string)将字符串转换为字符串或使用string.encode('utf-8')将其转换为utf8格式。如果你一开始觉得PHP和JavaScript中的=,==,===有点奇怪,当你在Python中使用引号时可能不会这么想。7.对象传递Reference大多数编程语言中函数参数的传递都是按值传递的。如果函数更改值,则结果不会传回调用代码。但正如我所解释的,Python恰好是不同的。Python默认使用pass-by-object-reference来传递函数参数。这意味着更改源变量可能最终会更改值。这是过程式、函数式和面向对象编程语言之间最大的区别。如果每个变量都通过对象引用传递,并且变量中的任何更改都会更改所有引用,那么您可能正在使用全局对象。用不同的名字调用同一个对象不会改变对象,所以它实际上是全局的。另外,正如C程序员早就知道的那样,全局变量令人厌恶,不要使用它们。在Python中,必须按值传递变量,例如“a=b”只是将另一个名称分配给同一对象空间,但不会将b的值复制到a。如果你真的想复制b的值,你需要使用一个复制函数,通常是“a=b.copy()”的形式。但是请注意,我说的是“通常”。并非所有数据类型都有“复制”原型,或者复制功能可能不完整。在这种情况下,您可以使用单独的“复制”库:“a=copy.deepcopy(b)”。8.本地命名以程序使用的库或函数命名程序是一种常见的编程技术。例如,如果我正在使用名为“libscreencapture.so”的C库测试屏幕捕获程序,我会将程序命名为“screencapture.c”并将其编译为“screencapture.exe”。gcc-oscreencapture.exescreencapture.c-lscreencapture在C、Java、JavaScript、Perl、PHP等语言中,这通常效果很好,因为这些语言可以很容易地区分具有不同路径的本地程序和库。但是Python呢?算了,别做了。为什么?Python假定您首先导入本机代码。如果我有一个名为“screencapture.py”的程序使用“importscreencapture”,那么它会导入自身而不是系统库。至少,您应该调用本机程序“myscreencapture.py”。Python是一种非常流行的编程语言,拥有许多粉丝。甚至我的很多朋友都喜欢Python。这些年来,我和他们讨论过这些问题,每次他们都点头表示同意。他们并不反对Python存在这些问题,他们只是认为这不足以挫伤他们对这门语言的热情。我的朋友经常提到那些非常酷的Python库。我同意有些图书馆非常有用。例如,BeautifulSoup是我用过的最好的HTML解析器之一,NumPy使多维数组和复杂数学更容易实现,而TensorFlow非常适合机器学习。但是,我不会仅仅因为喜欢TensorFlow或SciPy而用Python创建一个单一的程序。我不会为了这些“小便宜”而放弃可读性和可维护性,这不值得。通常当我写一篇关于某个主题的评论时,我也会尝试写一些积极的东西。但我不能列出Python的优点,因为我真的认为Python很糟糕。原文地址:https://www.hackerfactor.com/blog/index.php?/archives/825-8-Reasons-Python-Sucks.html【本文为机器之心专栏原文翻译,微信公众号《机器之心(id:almosthuman2014)》】点此阅读作者更多好文
