【外评】Python 为何如此糟糕…

上周,我在 LinkedIn 上看到一篇善意的帖子,它试图建议初级开发人员学习哪种语言才有机会找到工作。 其中排名第一的语言是 Python。 这当然是个错误。

或者是吗?

我不得不勉强同意帖子作者的观点,即 Python 超级流行,精通 Python 将有助于你找到工作。 然而,Python 的持续流行是一个问题,它的持续使用会让我们的行业倒退好几年。

我之所以做出如此苛刻的判断,是基于多年运行用 Python 编写的大型应用程序的经验。 在大型应用程序中使用 Python 就像用乐高积木搭建核反应堆一样: 是的,它有鲜艳的颜色,而且上手非常简单。 但现在反应堆已经运行了一段时间,到处都有辐射泄漏,却没人敢碰任何东西。 相反,人们到处贴上新的砖块来维持运转。 实际上,唯一能做的就是用混凝土将反应堆密封起来,让它冷却下来,然后用适当的建筑材料建造一个新的反应堆。

当然,Python 的问题在于它是一种解释型语言,具有动态类型和鸭子类型: 我们键入一些代码,保存起来,然后在运行时才会发现,根据我们输入的数据,一组语句是总是有效、有时有效还是根本无效。 通常情况下,在编写函数时,你只能部分控制进入函数的数据。 这意味着我们需要有纪律,详尽地检查所有输入数据,而历史表明我们并不擅长这一点。 更糟糕的是,Python 的 duck typing 会招致聪明但糟糕的代码,这些代码不可避免地会反过来咬我们的屁股。

在我为大型 Python 应用程序提供支持的这些年里,我看到并经历了一些可怕的事情,如果我们的应用程序是用一种理智的、编译过的、类型安全的语言编写的,这些事情就不会发生。

几年前,我曾设法说服我们的组织用 Rust 重写我们的系统。 结果证明,这是一个神来之笔。

我曾多次在生产中启动一个大型 Python 应用程序的新版本,但马上就被错误淹没,这些错误是由异常引起的,而异常又是由 Python 代码引起的: 没有在开发人员工作站上运行过,没有在单元测试中运行过,也没有在集成测试中运行过。

有些 Python 捍卫者会说,这不是语言的问题,而是代码审查和测试策略的问题。 他们错了。 理论上可以查看每一行并测试每一个输入和每一种情况,但这并不意味着实际上可以做到。 一门好的编程语言的价值之一在于,你不必查看和测试内存中每一种可能的位排列,因为它能保证大量的排列不会发生。 如果我必须详尽地审查和测试每一个 “a = b + c”,我的程序将永远无法投入生产。

我经常在查看 Python 函数时想知道是否有任何东西或任何人实际调用了这些函数,如果有,又是用什么参数调用的? 由于编译器和链接器不需要对整个程序进行全局查看,我常常不得不对代码库进行全文检索,看看能否找到任何调用点。 不幸的是,即使搜索不到任何结果,当我删除函数时,程序还是会崩溃。 即使程序没有立即崩溃,程序是否会在某些边缘条件下崩溃(如果该函数仅在该边缘条件下使用)也往往无法确定。你永远无法确定,这就是为什么不使用函数会导致程序崩溃的原因。

Python 的辩护者会说,这是一种糟糕的编程模式。 他们当然错了(又错了),因为一门好的编程语言的价值就在于它不会让你写出结构上有缺陷的程序。 诚然,在创建二进制程序之前,检查一下你所需要的所有代码是否都已存在,这难道不是一个过分的要求吗? 而造成这些问题的不良编程模式,往往是为了解决 Python 缺乏严格类型和在运行时找出是否有意义的能力所导致的重构问题而出现的。

Python 的另一个问题是性能。 我的笔记本电脑有 10 个 CPU 内核,Python 应用程序大约能有效使用其中的 1.2 个内核。 怎么办? 幸运的是,20 世纪 80 年代可以提供帮助! 我曾经用 Python 开发过一个服务,它可以分叉工作进程来处理请求,确保所有内核都能被使用。

不幸的是,这些 Worker 很快就耗尽了内存,因此我们决定让 Worker 在处理了一定数量的请求后自行终止,这样 Linux 就能帮我们管理内存了。 这本身不是 Python 的问题,但 Python 使问题变得更糟。

不过,分叉还有另一个效果: Python 对引用计数的使用破坏了 “写时拷贝”(copy-on-write)功能,因为为了操作引用计数,即使是只读变量的内存块实际上也会被写入,从而导致工作者的合并物理内存足迹爆炸。 为了解决这个问题,我们在解释器中设置了一个神奇的引用计数数字,用于主进程创建并由 Worker 继承的所有变量,然后就不会触及具有神奇值的引用计数了。 这是个超级聪明的解决方案,但应该没有必要。 如果你需要黑进解释器才能让它真正为你工作,那么简单的解释型语言又有什么用呢?

Python 给我带来了巨大的痛苦。 它像糖浆一样缓慢,而且显然不适合任何大于 100 行代码的程序。

为什么人们一直在使用它?

人们想要使用 Python 的首要原因是它 “易于使用”。 除了事实并非如此之外,这也是一个谬论,表明人们完全不了解总拥有成本 (TCO) 是如何运作的。 对于任何一款投入生产的重要软件来说,80% 或更多的成本都是在初始开发之后的阶段。 这是我们运行、调试、添加功能和重构的阶段。 如果剩下的 80% 都是调试和发现问题的地狱,而任何 20 世纪 60 年代的编译器都能发现这些问题,那么谁还会在乎前 20% 的速度快了一倍呢?

很多人祝贺您的编程语言简单易用,但这并不是您的工作。 您的工作是编写符合要求、可靠、易于维护和快速的软件。 Python 使得编写可靠、易于维护和快速的代码变得非常困难。

问:那我们为什么还要用它呢?

答:一个词: 虚假经济。

不幸的是,编写软件很难,但使用好的编程语言却能让编写半成品程序变得容易一些,通常是以增加飞行阶段的复杂性为代价的,而这一阶段总体上并不重要,但却是第一位的,因此在人们的心目中占了很重的分量,对我们有缺陷的项目规划方法有着非同寻常的影响。

使用 Python,你可以打开编辑器,写几条语句,关闭编辑器,然后运行代码。 这看似非常高效,但仔细一想,这就相当于开始建造摩天大楼时只砌了几堵预制墙(根本不用考虑地基),然后马上就想上厕所。 这不是我们建造摩天大楼的方式,也不是我们应该建造软件的方式。

“但 Python 作为其他库的粘合代码非常棒”。 不,不是这样的。 它并不伟大,它就在那里! 除了大量的现有软件可以让 Python 变得可用,以及很多人已经患上了斯德哥尔摩综合症之外,Python 并没有什么特别好的地方。 Python 就相当于 QWERTY 键盘,它糟透了,是导致 RSI 和腕管综合症的主要因素,但每个人都已经习惯了它,所以让我们用这种布局再造一个键盘吧。

就我个人而言,我把所有的休闲编程都换成了 Go。 它几乎和 Python 一样易于编写,类型安全,拥有快速的构建系统,并能生成高度优化的本地代码二进制文件。 当然,Go 并不完美(提示:没有一种编程语言是完美的),但如果你想编写可靠、快速的代码,并且在代码失控时至少有机会进行调试和重构,那么它比 Python 好得多。

另外,我不想说得太细,但如果你会 Python 而不会 Go(或其他像样的编程语言),你可能就没有资格以编写软件为生。 我知道这听起来很苛刻,对此我深表歉意,但编写软件是一种职业,因此你应该具备使用专业工具的知识、技能和经验。

Python 非常糟糕。 但它有一个可取之处: 它是荷兰人发明的! 🙂

本文文字及图片出自 Why Python is terrible...

你也许感兴趣的:

共有 121 条讨论

  1. 我希望人们能诚实地说,他们出于个人或美学原因不喜欢某种语言、框架或操作系统,而不是把它归结为客观上的坏,但我想可能没有人会点击 “我不喜欢 Python,有一些小毛病要挑”。

    哦,他只是在最后说了本该安静的部分:

    >还有,我不想说得太细,但如果你会 Python 而不会 Go(或其他像样的编程语言),那你可能就没资格以写软件为生了。我知道这听起来很刺耳,对此我深表歉意,但编写软件是一种职业,因此你应该具备使用专业工具的知识、技能和经验。

    数据科学家、flask 开发人员、系统工程师和 ML 人员都听到了吗?Python 很糟糕,所以你们应该放弃。)

    1. 在我看来,这种人即使非常聪明,也是极其刻板、不折不扣、一毛不拔的人。我从事这一行的目的是为了成功、建设和创造,这里面有很多 “能量 “方面的东西,而他的能量却死气沉沉。这是一个非常简单的事实,有些人看得见,有些人看不见。他就是一个看不到的人,而所有看到的人都能看得一清二楚。我绝对不希望我的团队中有他这样的人。

      他这句话中的白痴……含义之多,让我肉痛不已。不过,在与其他团队的程序员合作时,我经常会遇到这种不适应的无意识,所以我已经习惯了。

      1. 他在 6 月份发表了一篇题为 “不友善并不酷!”的文章。

        有趣的矛盾。

        我认识一个这样的人,精力充沛,满脑子想法,但却非常固执,我敢肯定这让他退步了。

        1. 笑话,bragr 没有提出任何想法,只有暴躁的抱怨。谁在乎呢?

    2. 啊哈,你指出了病症所在。

      这是软件领域最大的问题,而且有点难以解决。

      理想世界中的工具能让每个人做他们需要做的事情,这在某种程度上必须包括编程这样的活动。

      但是,”以编程为生的人 “有强烈的动机去把关,这可能是无意识的。

      1. 令人惊讶的是,以编程为生的人通常会被认为是 “专家”,但他们却对以编程为生的人所从事的各种不同类型的编程以及他们用来进行编程的工具知之甚少。

        这不仅是把关不严,也是程序员缺乏经验的表现。

        Python 程序员有一点值得称赞,他们往往直接与最终用户打交道,并构建与他们息息相关的解决方案。做过这种工作的人都知道,无论使用哪种编程语言,都很困难。

        1. 可以说,这是更 “重要 “的工作,或者至少是最需要关注的工作?

          我知道这很难开口–但讽刺的是,一个真正优秀的 “低级程序员 “注定默默无闻,因为事情只是在运行,一旦运行起来,人们就会忘记它。向 Linus Torvalds 致敬。

        2. 你是说专业化不好,专家不能成为专家。

          1. 不,我是说发表这些无知言论的人不是专家。

    3. 很明显,他是一个从未掌握过 Python 的围棋爱好者。而且,真的是 Go 吗?如果你推崇的是 Rust,也许我还能给你点怀疑的好处。

    4. > 我希望人们能诚实地说,他们不喜欢某种语言、框架或操作系统是出于个人或美学的原因,而不是非要把它归结为客观上的坏,但我想可能没有人会点击 “我不喜欢 Python,有一些小毛病要挑”。

      是啊,这种夸张的标题文章重复的大多是死马当活马医的论点,只是低效的点击率,而不是对任何讨论的严肃贡献。它为这场老生常谈的辩论增添的只是……事实错误,比如声称 Python 突出的特点是 “懒惰的评估”(虽然在任何语言中,你都可以在 Python 中安排 “懒惰”,但它通常是非常渴望的)。

    5. 我真的认为 Python 并不是一种适合 ML 的语言,它只是先 “到达 “了那里。

      当然,生态系统才是真正的优势。但这门语言却让人头疼。我同意 “虚假经济 “的观点。我很乐意用动态 “胶合 “的 “敏捷性 “与某种真正的类型安全性、人类可读的错误信息和性能[0]进行交换。

      [0] – 将 C 隐藏在 Python 的外衣中不算:)

      1. Python 是事实上的胶水语言,拥有最大的生态系统之一,这使得使用任何类型的越级库成为可能,这些库在一次 “import antigravity “后可以做 1093 件事。此外,ML 对 python 绝对有意义,因为大多数 PL 并不支持显卡–ML 是非常具体的数据操作(一项基本的动态任务)和调用特定库进行训练(一项非常胶合的任务)。在这方面,任何语言都比 Python 好。

        引用布鲁克斯的话(已被篡改): “唯一能显著提高生产力的方法就是依赖已经编写好的代码”。你花哨的 “更好的 “语言连 Python 的十分之一都没有,它无法取代 Python。

        1. > Python 是事实上的胶水语言,拥有最大的生态系统之一

          我从未质疑过这一点。

          > ML 是一种非常具体的数据处理语言(从根本上说是一种动态任务)。

          我强烈反对使用动态语言处理数据。数据有维度、单位和类型。你需要知道,你并没有把大衣加到马匹上,也没有把美元加到欧元上。你需要知道你没有在错误的坐标轴上默默地切片。您可能需要正式验证。您可能需要转换数据,而不必担心无声的错误。

          Python 中所有的 “元数据 “和封装类 ML 都只是试图给你提供语言所不能提供的东西。

          > 这是一个非常粘合的任务。有什么语言比 Python 更好的?

          这正是我的观点。ML 的发展已经超出了粘合几个 C 库的范畴。它需要复杂的大型程序,而这正是 Python 最不擅长的领域。此外,”粘合 “组件的不同性质(每个组件都有自己的数据格式、协议和调用约定)也使得粘合成为无类型混合魔法习语的混合物。

          1. 训练和使用 ML 则不同。将训练好的权重与不同的编程语言系统捆绑在一起是比较常见的做法–但训练可能是一个更具探索性的阶段,因此 python 也不失为一个合适的选择。

  2. > 如果你会 Python 编程,但不会 Go(或其他像样的编程语言),那么你就不可能以编写软件为生。我知道这听起来很刺耳,对此我深表歉意,但编写软件是一种职业,因此你应该具备使用专业工具的知识、技能和经验。

    软件工程职业的一部分就是维护已经编写好的软件。难道维护 python 代码的人就不应该获得报酬吗?

    另一部分工作是选择合适的工具。Python 有其缺陷,但在某些方面比 Go 更好。例如,它拥有更丰富的库生态系统。

    1. 让我问一个显而易见的问题。

      为什么由专业软件工程师组成的围棋社区没有建立一个更加丰富的库生态系统?

      是厌倦、无能还是态度问题?

      由于 Go 来自 Google,人们的态度是否是:”我是专业人士,我只需要写自己的代码来解决 X 问题”,而不是考虑建立一个可供他人使用的库?

      在 Go 中构建库更难吗?Go 社区对库的采用是否有所不同?

      这是一种思维定势,即库是无趣的?

      还是完全另有原因?

      1. 比较一下 Python 和 Go 的程序员小时数。有 15 年的领先优势会有很大帮助。

        另外,我用 Go 进行专业编程已经有一段时间了,我已经有一段时间没有找到关键库了。

        无论如何,Go 最终需要的库更少;在 Python 中,你有纯 Python 版本,不,等等,Twisted 版本(可能不是最新的,但仍然都在,增加了库的数量),不,等等,async 版本,不,等等,Python 2 版本,不,等等,绑定到 C 库的版本….,实际上这并不是 Go 特有的,而是 Python 特有的。几十年来,当 Python 转用 async 时,所有与 async 相关的非 async 库都突然需要克隆,因此库的数量变得越来越多。Go 的语言实际上没有发生过创建此类并行库的变化(注意是变化,而不是添加;Go 的添加创造了创建更多库的机会,但并没有创建大量并行库)。我不知道 Python 是否是唯一不断变化的语言,”最佳实践 C++”可以说是其中的佼佼者,Javascript 虽然也有很多变化,但它属于此类语言中的高层。从数量上看,那些不会出现这种变化的语言应该可以用更少的库来满足同样数量的 “需求”。

        1. 我曾在专业领域使用过围棋,我认为这不仅仅是一点点复制,而是相当可观的。

        1. 这倒是真的,但我看到的用于机器学习、张量、LLM、等同于 PyTorch 的 Go 库在哪里?

          还有人说他们不需要 Go 中的这些库,或者说 Python 中有大量的库,这些库都是过时的或特定语言版本的残渣。

          那为什么 Go 不是机器学习、分析、快速原型开发、数据转换、ETL 等的首选语言呢?

          相比之下,我听说 Rust 语言的语法和学习曲线对于非计算机科学家或系统程序员来说有悖常理。

          这样看来,非计算机科学人员会选择一种更容易上手的语言,这种语言的生态系统能让他们在短时间内完成工作。

          我想知道的是,Go 语言的专家和忠实用户或推动 Go 语言发展的语言架构师们是否认为有必要采取并行的方式,让 Python 爱好者加入进来,或者让 Go 语言成为人们的首选语言,而不是 Python 或 TypeScript 等语言,因为 Go 语言提供了同类最佳的方法,可以作为底层功能快速构建原型并扩展到架构良好的系统中。

      2. 为什么人们使用 x86 上的 Microsoft Windows?

        计算机行业有一个怪异的名声,那就是发展迅速,打破常规。事实上,该行业的保守程度令人震惊。你会遇到很多很多完全拒绝学习新事物的程序员。

        1. 大多数公司和大学都没有接触过最前沿甚至现代的技术。他们停留在自己的泡沫中。

          离开硅谷后,我发现工程管理文化可能不喜欢培训,也不允许通过开发项目积极培养技能。

          只要用 Java 或 C++ 编写代码就可以了,如果你没有接受过适当的 SQL 培训,甚至不了解最佳实践,那也没有关系。

          这种文化会导致一些可怕的产品实施和升级情况。

          见鬼,我们的 IT 部门就禁止访问这个网站。

          我只能通过个人设备访问。

      3. 围棋社区是什么?谷歌员工?根本就没有 Go 社区,它只是一种企业语言。

        为什么 Visual Basic for Office 社区没有开发出更多的库呢?

      4. 不知道是否只是修辞问题,但 Go 有 C 语言的表现力,我们不要把它和经常是教科书式伪代码的 python 相提并论。

        1. 如果 “C 语言的表现力是如此美妙,为什么还要用 Go 呢?为什么 Java 和 J2EE 会被推广到企业开发中?

          可能是因为用 C 语言编码过去和现在都很困难,而且内存管理指针问题和其他问题导致代码不稳定?

          1. 我把我的评论写成了否定句–C 非常不善于表达,就像围棋一样。

  3. 可怕的是使用一种动态编程语言并期望它具有静态特性。此外,衬垫和类型提示已经有了长足的进步。

    1. >此外,衬砌机和类型提示已经有了长足的进步。

      你承认在 Python 中可行的静态分析是有价值的,但想要不可行的静态分析是 “可怕的”。有趣的是,这两条界限完全一致。

      1. 如果你想要静态类型,通常你会避免使用 python。

        如果你想自由地轻松处理 json 或其他动态类型,你会喜欢 python。

    2. 他们有,但我发现他们仍然落后于技术发展水平。Python 坚持对类型提示的可选、二次思考支持令人沮丧。

      当然,Linting 不止一次地救了我的命。

    3. 在很多情况下仍然非常糟糕。与那些已经存在了几十年的东西相比,它的速度非常慢。做一些 ocaml,你就会发现 “inted “是多么令人难以置信的糟糕。你可以在几秒钟内编译数百万行的 ocaml,在 90 年代的电脑上编译德尔菲,Jai(我不觉得 Blow 有同情心,但他确实指出了一切是多么蹩脚,这很好)等等,但当我的 50k python 或 typescript 项目在进行 yarn 编译时开始着色时,我就有时间好好锻炼了。

      在我看来,Ocaml 是一个有趣的例子,因为它的类型推断引擎非常出色,你几乎不需要指定类型。当你眯着眼睛读它时,它看起来是动态的。其实不然。

  4. 有一种蒸发冷却效应。十年前,Python 在多核世界中的日子显然不好过。那些需要这种性能并且知道自己需要这种性能的人在这几年间离开了。显而易见的是,无论 Python 做什么,它都无法解决这个问题。现在这些人已经不在 Python 社区了。

    剩下的是那些不需要这种性能的人,有时我就是这样的人,我仍然乐意使用 Python 来做一些事情;还有那些需要这种性能,但却不知道的人。这些人才会陷入困境。

    我确实希望 Python 开发者社区能更坦诚地认识到,Python 的性能在很多情况下确实非常糟糕,虽然有一些工具可以 “啄边”,但从根本上说,它仍然是一种低性能语言,尽管有 NumPy 这样的工具(归根结底,它仍然是一种 “啄边 “工具,即使它啄的特定边做得非常好,但当你脱离了它的加速代码路径时,这只会让性能三角洲变得更糟)。我觉得,也许在 200 年代,整个社区对这一点更加开放。而现在,”Python 很慢 “被社区视为一种攻击。也许是因为了解这个问题的人大部分都不在了。

    但在 2020 年代,是的,Python 应该被直接淘汰,因为人们现在想做的许多任务都是基于直接的性能原因。在单核时代,克服 ~40 倍的劣势有时很困难,但通常是可以做到的。但现在,基本上每个系统核心的性能都会成倍增加,如果你需要的性能是这样的水平,那么克服数百倍的劣势是不值得的。Python 是不错,但还不足以为多百倍的性能损失买单。自 1995 年以来,静态语言已经有了长足的进步。

    1. 如果 Python 唯一的问题是速度太慢,我还能接受!它的其他问题更糟糕(比如工具、糟糕的类型提示等)。

      1. 同意,但我认为如果不把 Python 变成 Python 之外的东西,就不可能解决速度慢的问题。从 Python 诞生那天起,我们就被许诺,”足够智能的编译器”(Sufficient Smart Compilers)会出现,让 Python 变得和 C 语言一样好用。我们知道现在的极限是什么样子的,基本上就是 PyPy;除了在小范围内,它无法达到 C 语言的水平,而且在这个过程中会消耗更多内存,我们没有理由相信它会发生。

        Python 的语义从根本上就很慢。要解决这个问题,需要改变语义。这样的改变会使 2 -> 3 的大小相形见绌,而且实际上是一种新的语言,就像 “Perl 6 “一样。

  5. “只有两种语言:人们抱怨的语言和没人使用的语言。

  6. 每一个 “这种语言很好 “或 “这种语言很糟糕 “的观点,都需要加上一个 “到底是为了什么”。

    “这种扳手用来敲钉子真的很糟糕!”

    1. 同意。但我也认为,基于一般理由批评通用语言是公平的:)

      只是这篇文章在这方面做得不太好。

      1. 这很公平,但另一件事发生得更多。

        老实说,这让我觉得很奇怪,比如说,”使用多种语言就好了 “这样的话虽然说得很多,但并没有被当作一个好主意来讨论(就像 “我的语言好,你的语言不好 “一样)。

    2. 这篇文章的作者确实强调了使用 Python 并不顺利的情况,事实上是多次。

      1. 作者当时工作的环境是一家大型企业,需要管理一系列苛刻的服务。管理这些服务器的许多工具都是用 Python 编写的,因为很多人都知道 Python,而且 Python 已经在组织中广泛使用,它对 C++ 互操作(也在组织中广泛使用)有很好的支持,而 Java 被认为是非常笨拙的系统管理工具。

        我见过这两方面的情况–我和乔斯在同一个子组织工作,我的第一份工作是清理一大堆极其重要且质量参差不齐的 Python 代码,这些代码用于管理数据库服务器。或者在所有这些主分片上应用模式变更。或者在运行时监控分片。

        有时,在一次重要的例行维护中,代码会发生异常(字面意思是 Python 异常崩溃),导致迁移工作半途而废。我被雇佣–真的,这就是我的工作–为代码添加测试,直到它变得更加可靠。这项工作让我相信,比起类型安全(这很好,在 Python 中可以选择使用),高测试质量和生产中使用的路径的高测试覆盖率对保持代码顺利运行更重要。

        我只希望 Python 没有把字符串作为序列类型,因为在组织中最常见的错误之一就是在 To: 字符串中不小心输入了 “s i n g l e “字母。例如,如果是 “To: bore-sre@stoogle.com“,那么 b@、o@、r@、e@ 等人都会收到一封邮件,上面写着 “处理失败……”。而 r@ 会回复(因为他是 rob pike)说:”你的 python 程序有一个错误….”。

  7. Python 是我最不喜欢的语言之一,我尽可能地避免使用它。我同意这里的一些批评,但我不同意这一部分:

    > Python 的问题当然在于它是一种解释型语言,具有懒惰的评估功能。

    这不是 Python 的 “问题”。这类语言并没有什么问题。然而,它确实限制了这种语言适合处理的问题的种类,我确实看到在 Python 被使用的地方,另一种语言会产生更好的结果。

    也许 Python 使用不当会导致某些人认为问题出在语言上?

    1. > 懒评估

      我知道 Python 有懒惰的函数,但它并不像 Haskell 那样懒惰,对吗?我从来不用它,因为我觉得它是一种可怕的恐怖表演(这是我的口味,其他人喜欢也没关系),但我不得不用它几次,并发现(也是从文章中看到的)某些部分是懒惰的,但不是作为语言的 python。这不对吗?

      > 它确实限制了该语言适合解决的问题、

      解释(……)就是实现,没有理由要解释。

      1. 你说得对,这篇文章错了。f(g(),h())总是同时调用 h 和 g,而不管 f 是否使用了它们的结果。

        迭代在某种意义上可以是 “懒惰的”,但这种懒惰是通过建立在严格的核心语言之上的数据结构实现的。Python 并不是唯一拥有懒惰可迭代数据结构的语言,但它在标准库中相对较多地使用了这些数据结构。

        我们中的许多人都喜欢这种结构,因为它能让 for 循环在各种抽象之上优雅地运行,但我也能看出有些人不喜欢这种结构。不过,作者并没有解释这种细微差别,这让我觉得他并不完全了解自己在说什么。

        1. 几乎每种主流语言都有 “懒惰地”(你所指的)迭代列表的方法,所以这不可能是作者特别强调的。我认为他只是弄糊涂了。

          1. 但这只是库。从外到内进行实际评估的语言则不同。是的,我确实感到困惑。

      2. 几乎可以肯定,TFA 的作者指的是 “动态”,而不是 “懒惰”。

        没有人会在 TFA 中使用 “lazy “这个词。

        1. 这完全是错误的。维基百科链接指向的是 Haskell 意义上的 “lazy”。Python 并非如此。

          1. 同意。这显然是错误的,我猜作者认为 “lazy “的意思是 “动态的”,于是搜索了维基百科,并在没有阅读的情况下粘贴了文章链接(在这种情况下,他可能会发现这与他的理解不符)。

            这是一种不太疯狂的解释。另一种解释,即作者阅读并理解了什么是 “懒惰评估”,但仍然认为 Python 是这样做的,这就太疯狂了,无法考虑。

        2. 这篇文章的意思是 Dynamic 而不是 Lazy? 啊……那么,这就是糟糕的用词了。

          1. 是的,是的。

            如果一开始就严重误用一个专业术语,那就不是批评的好兆头了。

      3. 这不是 Haskell 风格的 “懒惰”,而是声明只是另一种语句,在执行这些行之前不会被评估。这意味着你可以在 if 中定义函数,这对条件编程/元编程非常有用。同样,所有变量的访问都是在运行时进行的。因此,你无法真正静态地验证程序,唯一能确定程序运行情况的方法就是运行它。

        一个奇怪的现象是,函数参数默认值是在函数定义执行时而不是在调用时进行评估的(所以是半路偷懒),因此作为默认值的 `[]` 是该函数所有用户共享的,如果你修改了它,就等于改变了一个全局。我听说这导致某些安全软件出现漏洞。

        1. 你所描述的并不是任何公认意义上的懒惰评估。我敢打赌,这也不是作者的意思;他可能想到的是动态类型。

          或者,作者(和你)的意思是 “解释型语言”?懒惰/严格评估是一个正交的方面。

  8. 是的,我一字不差地同意你的观点。以任何可靠的方式重构 python 应用程序都是非常非常困难的。python 处理错误的标准方式似乎是向用户提供堆栈跟踪。这对用户非常友好(不是!)。现在,人们会说,例如,mypy 可以在这方面提供帮助。这没错,但由于项目启动时可以不进行类型检查,因此你的项目启动时很可能没有进行类型检查,而且引入 mypy 的工作也被积压在某个地方,当它从积压中解压时,也只能部分启用,否则就会有太多错误等等。这就是一个垃圾编程环境。

      1. 无论你是否同意,异常处理而不是返回值的论点是,它能让代码在成功的情况下更自然地流畅运行。 另一种选择是围棋的重复性

            f, err := fn(...)
        if err != nil { ... }

        模式。孰优孰劣其实是一个品味问题。在我看来,将错误处理分散在代码各处显得更不优雅,也更凌乱。

      2. 你可以返回异常而不是引发异常。因为语言是动态的,调用者可以自省。

        就我个人而言,从数字解析函数的角度来看,我认为传递一个无法解析的数字是一种 “异常 “情况–InvalidArgumentError,我不在乎以哪种方式返回–是作为引发的异常,还是作为错误对象–只要有明确的文档记录,并且使用方式与函数的语义相匹配(NetworkNotAvailable 是一个更好的异常示例,在这种情况下,你需要在异常块中添加一些内容)。

    1. > python 处理错误的标准方式似乎是向用户提供堆栈跟踪。

      你希望它做什么?默默地失败,然后继续?

      1. 既不是 “无声地失败”,也不是 “打印堆栈跟踪”,那这样如何?让程序员来处理错误如何?

        1. 是啊,Python 应该引入 try except。

        2. Python 早在 20 多年前就有适当的错误处理了。

  9. 几周前,我正在开发一个小型的野火烟雾和火灾周边应用程序接口,由于工具问题,我遇到了一些恼人的障碍。我需要处理大量不同格式的分层地理数据集,而将一种数据转换成另一种数据、将数据处理成各种数据桶、清理、汇总等工作都非常繁琐冗长。

    我写过很多 Go 语言,对此已经习以为常。但是,当我在不那么熟悉的地理数据处理领域遇到困难时,就会觉得很费劲。糟糕的库文档是一个主要障碍,除此之外,基本功能似乎根本不存在,我不得不去发明它们。

    我萌生了在项目中探索 Python 的想法,因为人们使用 Python 进行数据处理。我过去也用过 Python,但从未用于这个项目。我想,不管怎样,至少我可以验证 Go 是一个合适的工具。

    不到一天,我就用 Python 重建了一切。我围绕预报和火灾周边工具构建了一个 flask 应用程序,并在当天晚上将其部署完毕。这让我大吃一惊。

    作为一个生态系统,Python 绝对让我大开眼界。我喜欢这种语言吗?不太喜欢。我遇到过很多这样的情况:使用 Go 语言可以更快、更高效。部署会更容易。我可以从同样的资源中获得更多的 API。扩展会容易十倍。静态类型工具总是把我的集成开发环境搞得一团糟,因为这个库不支持这个,或者类型工具搞错了那个。有时这非常不方便。

    然而 Python 却做到了。现在 Python 已经上线,开发工作也很稳定。Go 并不稳定,如果不重新发明无数的轮子,我看不到任何解决方案。

    1. 顺便说一句,python 的另一个好处是,你可以在运行时强制程序进入带有交互式 shell 的调试器,并检查/打印对象。在如今这个泛型复杂的世界里,通常很难看清到底是哪个方法在处理你的数据。

    2. 我最喜欢 Python 的地方是它庞大的社区和快速的迭代。别误会,我也喜欢 Go。在我之前的工作中,我主要使用 Go 语言。但有时你需要的只是庞大的社区和大量的文档,或者你想让某些东西现在就能工作,而不是在那里瞎折腾,Python 就非常适合这种反馈循环。

  10. 我使用 Python 的经验可以概括为:开始使用 Python 时很有诱惑力,因为它的(初始)摩擦很低,而且 “反正这只是一个小项目”。

    几个月或几年后,它就变成了一头野兽,难以理解或重构,充满了漏洞和陷阱,而 Python 糟糕的工具也无济于事。

    而我却从未吸取教训!

      1. 我以前在 HN 上也遇到过这种情况,所以我觉得现有的工具完全不能让我满意。

        衬垫还可以,偶尔还能救命(但根本不需要!它需要额外的工作来解决其他语言 “免费 “解决的问题)。而且它不应该是一个单独的工具。使用起来也很麻烦,不需要的东西要静音(太吵了),还要进行微调。针对特定警告启用/禁用它的内联注释看起来也很难看。Python 开发人员倾向于压制任何困扰他们的问题,而不是去解决它,因为这不符合他们的文化。

        类型提示检查非常糟糕。虽然已经有所改善,但它仍然会遗漏一些明显的东西,而且需要太多的手把手指导。根据我的经验,一般的 Python 开发人员不会使用它,因为他们不了解它,或者觉得投资回报率不值得。而且因为它是可选的,他们可以假装它不存在(或者在你强制使用它时抱怨)。

        我们已经多次讨论过依赖管理这个混乱的问题。在 Python 的辩解中,它与其他不同语言的混乱 “好 “同伴。但 Python 的情况似乎特别可怕。

        一般来说,在工具方面,Python 生活在一个特殊的地狱中,每篇博客和文章都会告诉你 “这很糟糕,因为你做错了,你应该 [使用|避免] pip、pyenv、pipenv、poes、<我的自定义脚本>、<某个别人都不推荐的过时工具>、<与其他一切都不兼容的尖端工具>”。

  11. 这篇文章的作者似乎是 Go 编程语言的布道者。

    > 如果你能编写 Python 代码,却不能编写 Go(或其他像样的编程语言),那么你可能就没有资格以编写软件为生了。

    1. 有趣的是(在另一位作者的另一篇 Python 评论中),有人指出,谷歌是 2000 年代早期 Python 的最大推动者。如果不是酝酿与甲骨文公司的法律纠纷,Java(来自 Android 和其他系统)可能才是最重要的推手。所以,Python 在这里,Python 在那里,Python 无处不在……后来,Google 发明了 Go 和 Dart 以及其他闪亮的新玩具,并开始到处推广。

      1. 在谷歌,编写 Go 是为了取代 C++ 和 Java,而不是 Python。但在它推出后,SRE 的许多 Python 开发人员都转用了 Go–这是有原因的。起初,Go 的创造者们对此感到有些意外。

        在谷歌,Python(面向 C++ 代码)仍然发挥着巨大的作用。我认为这种情况不会改变。Go 在科学计算领域的应用几乎为零。

      2. > 如果不是酝酿与甲骨文之间的法律纠纷,Java(来自 Android 和其他系统)很可能就是谷歌的目标。

        甲骨文于 2009 年开始收购 Sun(以及 Java),并于 2010 年完成合并。因此,我看不出这对谷歌在本世纪初,甚至是 2007 年或 2008 年所谓的厌恶 Java 有什么影响。

  12. > 如果你会 Python 而不会 Go(或其他像样的编程语言),那你可能就没资格以编写软件为生了。

    得了吧。这就是被人诽谤,还有这个。

  13. 我不是 Python 的粉丝,但这篇文章的语气让人难以忍受。如果 Python 不能满足你的需求,那就别用它。Python 有很多奇怪的地方值得抱怨,但这篇文章除了 “我喜欢的方法是唯一有效的方法 “之外,并没有试图学习什么或提出更大的观点。抱歉,这种观点不值得考虑。

    1. 虽然我个人很不喜欢 python,但我完全同意这个观点,只是想翻转一下。如果 pythong 能满足你的需求,那就绝对使用它。不要在下载本月最新版本的 python 时抱怨 Visual BASIC 如何不适合你。

  14. 没有强制的静态类型和适当的调试器,使得 Python 对于大型代码库来说非常痛苦。对于脚本、原型设计和将库粘合在一起制作实用程序来说,Python 是不错的选择,但如果某些东西扩展到一个文件之外,我就不想再用 Python 了。说服我的雇主相信这一点是另一回事,这也是我宁愿完全避免使用 Python 的原因。

  15.  Python 的问题当然在于它是一种解释型语言,具有懒评估功能 [...] [完]
    Huh?
      1. 当一篇文章的开头严重误用技术术语时,这可不是什么好兆头。
        我不想为 Python 辩护,但这篇咆哮的开头并不好。

        1. 我就是来发表这个评论的。但我没看到这条评论,所以我也说了同样的话。

  16. 因为有了 TensorFlow、PyTorch、LangChain 等库,我越来越喜欢用 Python 来编写小程序和脚本。
    我同意作者的观点,有更好的语言适合大型应用程序。

  17. 我用 Python 开发过非常复杂的软件,而且运行得相当不错。
    如果你希望在 Python 中找到 Java 或 C 语言,那你就找错地方了。

    1. 当然,现在它被废弃了,因为你是在一个不再支持的 Python 版本中编写的,或者使用了一个尚未(或可能不会)移植到 3.11 的库。

      1. 我做了 2 到 3 次迁移。还不错。我宁愿偶尔处理一下这类问题,也不愿每天都与 Java 打交道。

        1. 我做过 90kloc 的 python 移植。两次。我再也不为允许开发人员使用 Python 的公司工作了。
          我很高兴你能移植你的脚本,但 Python 绝对不适合用于关键任务软件。

  18. > 我曾经用 Python 开发过一个服务,它可以分叉工作进程来处理请求,确保所有内核都能被使用。
    > 不幸的是,这些 Worker 很快就耗尽了内存,因此我们决定让 Worker 在处理了一定数量的请求后自行终止,这样 Linux 就能帮我们管理内存了。
    我曾经用 Python 处理过一个服务,它本质上是一个图像服务器,用于处理一种特殊的生物医学图像格式。解码器是一个由供应商编写的专有 C 库,不出所料,它存在大量内存泄漏问题。正是 Gunicorn [0] 的这一特性,让我们省去了几个月与供应商来回奔波的时间,让这个库不再扼杀我们的服务器。
    Python 有它的缺陷,但人类接触过的任何东西都有缺陷。
    [0] https://docs.gunicorn.org/en/stable/settings.html#max-reques

  19. 说真的,对于 JS/NPM/Electron 的抱怨,与 Python 工具和 PyInstall 相比,它简直就是天才。
    让人极度沮丧的是,你不得不使用它来访问那些根本不使用 Python 的技术,实际上它只是将本地 C++ 或 GPGPU 代码粘合在一起的合成胶水。

  20. > 在大型应用程序中使用 Python 就像用乐高积木搭建核反应堆一样。
    我喜欢这种比较。无论如何,值得注意的是,作者是在 “多年运行用 Python 编写的大型应用程序的经验 “基础上得出结论的。静态类型的优点和动态类型或鸭子类型的缺点早在几十年前就已众所周知。问题并不在于 Python 作为一种语言,而在于随意使用它的决定。再举一个例子:是什么阻止了人们使用 “乐高积木”(或其高温证明版本)来建造反应堆?合理的工程决策,以及最重要的安全法规。

  21. >分叉工作进程处理请求
    把这个归类为 “UNIX 系统程序员认为原则上可行,但最终却成为一个巨大的黑洞,无法以一种合理的分叉后状态的方式来静止任何非小应用程序”。

  22. Python 是一门可怕的语言,但原因并不像作者所说的那样。range() 返回一个生成器并不意味着整个语言都是懒惰的。有几种 Lisps 允许类似鸭子类型的语言,它们并不可怕。用动态语言对程序行为进行推理是可能的,但 JavaScript 肯定会让推理变得困难。
    Python 是一种可怕的语言,因为它不是一种语言。它是一个每年十月都会变化的语言家族。当然,3.x 每个版本引入的向后不兼容的改动没有 2.x 那么多,但这就好比说第一次世界大战并不可怕,因为战争快结束时,似乎每周死的人都少了。

    1. 在过去 10 年的大部分时间里,我一直在用 Python 编程,我从未经历过次要版本之间向后兼容性的倒退。你遇到过什么问题?

      1. 每一个版本的语言语法都会以非向后兼容的方式发生变化。
        如果你有几个脚本,当然,也许你不会受到影响。但如果你收购了一家生产了 90kloc python 的公司,然后所有员工都辞职了,那日子就不好过了。
        当然… 我不应该使用那些功能 我明白 是我不好 因为我说你的孩子丑 尽管最初写那些代码的人不是我。
        虽然我确实写了一些使用包变量的代码。然后包变量的语法变了,但这很容易解决。然后包变量的作用域变成了类变量,这完全没问题,但更难找了。然后语法又变了,但更难找了。然后在几个版本中,如果启用了 async io,调试器就停止工作了。
        Python 完全是给业余代码爱好者准备的。

        1. 哪些语法特征发生了不向后兼容的变化?我遇到过一些小麻烦,别误会我的意思,但每次这些麻烦都是常见对象的接口变化造成的,比如异常类型。当然,Python 在各个小版本之间添加了一些语法糖,但从来没有以牺牲向后兼容性为代价。

          1. 包变量。类变量。语法在 2.3、2.5 和 2.7 之间发生了变化。语义也在 2.3 和 2.7 之间发生了变化。

            1. 哇,没想到人们还在积极开发 Python 2.x。顺便说一句,Python 指导委员会最近非常重视向后兼容性。下面是最近的一次邮件列表讨论,内容是委员会决定拒绝一个流行的 PEP,理由是它会破坏 Pydantic:https://mail.python.org/archives/list/python-dev@python.org/…

  23. 我曾经很喜欢 Python。它让我变得高效。
    直到它引入了杂乱无章的类型系统。现在,我需要在每个文件中导入类型,在每个文件中使用 IF 在 CI 中保护类型,并使用强大的集成开发环境来发挥类型的优势。

    1. > 现在我需要
      你不需要做任何事情,你可以忽略所有类型提示
      > 在每个文件的 CI 中使用 IF 来保护它
      你说的是 “if TYPE_CHECKING: “吗?
      您的另一个选择是在文件顶端写上 “from __future__ import annotations”,或者等到 Python 3.13 的 PEP 649 版本,类型注解就会变得易于评估了。

    2. 键入也不会给我带来任何快乐,因为无论如何,这都是大量的工作,却不完整。根据编程法则,未在运行时与实际行为进行对比检查的注解总是会有微妙的错误。
      我仍然使用 Python。在我看来,最近引入的匹配语句是一个很好的补充。

  24. 谁能给我解释一下这部分内容?我不明白这是怎么回事。
    > Python 对引用计数的使用破坏了写时拷贝功能,因为即使是只读变量的内存块也会为了操作引用计数而被写入,从而破坏了 Worker 的物理内存占用。为了解决这个问题,我们在解释器中设置了一个神奇的引用计数数字,用于主进程创建并由 Worker 继承的所有变量,然后就不会触及具有神奇值的引用计数了。
    谢谢

    1. 你有一个程序,由于某种原因(这里指的是 Python 运行时)只能单线程运行,尽管它的工作量可以很容易地并行化(比如,它是一个独立处理请求的网络服务器)。实现这一目标的老办法是启动一个 “主 “进程,分叉出 N 个 “工作 “进程,每个进程都可以单线程运行。
      如果需要的内存是单个进程的 N+1 倍,那么这种方法就不可行了,因此操作系统使用了一种名为 “写时拷贝 “的优化方法。当进程分叉时,它的所有物理内存都会被新进程共享,因此启动进程几乎不需要新的内存。如果新进程写入内存页,该物理页就会被复制,从而拥有自己的版本。(这就是 “写入时复制”)。
      对于大多数程序来说,这种方法都很有效,但如果运行时使用垃圾回收技术,即使代码没有改变对象的任何值,也需要写入对象,那么麻烦就来了。使用引用计数时,每当指向对象的指针被分配时,就必须为对象写入新的引用计数。如果将引用计数存储在对象中,就意味着必须复制对象的物理页面。因此,现在的 CoW 优化完全不起作用,因为仅仅引用对象就会占用额外的新内存。
      Ruby 曾经也有这样的问题,在 Ruby web 服务器流行起来之后(你好,Rails),他们最终加入了一个补丁,将 GC 信息移到了实际对象堆之外的地方。其他系统(如 JVM)也使用了类似的技术,将记账位存储在对象字段位以外的地方。
      因此,OP 所做的就是给运行时打补丁,使主进程(分叉前)中创建的对象拥有特殊的引用计数,并且永远不会更改。这在很大程度上是可行的,因为主进程通常会进行大量的设置,所以它的对象大多不会成为垃圾。

    2. 我不明白他提到的 “smurfing “解决方案,但 CPython 的运行时使用每个引用值中的引用计数来检测垃圾(当一个值可以被释放时),这意味着即使是只读值也会随着对象引用的来来去去而被运行时修改。
      这些修改会强制复制在分叉子进程时以写时复制方式创建的页面(这意味着它们共享同一个物理页面,直到子进程修改了该页面),从而使写时复制通常可以节省的内存消耗殆尽。

  25. 就像大括号能让语言变得更好一样:-P
    我对 python 的问题在于它的软件包系统,以及它在设计上的全局性。(我对 Ruby 也有类似的不满)。

  26. > 因为一门好的编程语言的价值就在于,它不会让你写出结构上有缺陷的程序。
    嗯……好吧。
    我不想在这里为 Python 打气(事实上我一点也不喜欢它,也尽可能地避免使用它),但作者的许多观点似乎都可以归结为 “螺丝刀不好,所以你应该一直用锤子”。
    不同的工具有不同的用途。

  27. Python 是一个吸引缺乏经验的程序员的重力池。而且很多时候(根据我的经验),这一点会显现出来。
    缺乏静态类型与缺乏常识和不愿学习(”何必勉强自己,反正就业市场会吞噬一切”)相比,简直不值一提。

    1. 说得好!换一种说法: 由于人们的使用方式,Python 并不是一门适合生产的语言。
      每当我发现某个项目无法正常运行,或者运行了几周后又停止了,似乎总是与 Python 有关。人们无法正确使用 semver(甚至连 python 本身也是如此?还是说有些程序能在 3.9 上运行而不能在 3.10 上运行又是程序员的错?同样的问题也可能发生在 nodejs 代码中,但我却很少看到这样的基本故障。
      我也不明白为什么会有人愿意使用 python。我试过调试 python 代码,也试过编写新的 python 代码,但我总是无法融入其中,也不容易读懂别人写的代码。空白敏感性?用于软件包的特殊___文件?没有 JIT(不知道为什么 pypy 是一个独立的东西,而不是整合在一起)?与 JS 相比,我实在看不出它有什么优势。对于其他语言的用户来说,JS 甚至还具有语法直观的优势,而 typescript 确实将 JS 从混乱中拯救出来。在语法上与 C 不同也没什么,但我不认为 Python 有什么好处,能证明这种语法占用了我大脑中的另一个位置,而我本可以用它来学习 rust 或其他语言。

    2. > 缺乏静态类型与缺乏常识和不愿意学习相比简直不值一提。
      我从未发现有哪个语言社区缺乏这种现象,如果有的话,那么更多的是那些有丰富经验的人只使用一种语言,而不是那些没有经验的程序员在学习他们的第一门语言(从本质上讲,他们往往有学习的意愿,即使他们缺乏被称为 “常识 “的经验基础)。

  28. 如果你使用的时间足够长,任何东西都是可怕的。对于某些用例来说,有些东西比其他东西更糟糕–深思熟虑的开发者会了解工具的弱点,并为手头的工作选择合适的工具。

  29. 鉴于最近用于 ML 和数据科学应用的 Python 大量增加,这个时机很有意思。
    Pytorch 也是一个很棒的库。很难想象 Python 的使用会在短期内减少。

  30. 哇,说 Python 程序员不专业….,太不可思议了。完全和现实世界脱节了。
    我很庆幸我在谷歌的时候从来没有向他汇报过工作。

  31. python 的问题在于,它将一大批对编程或计算机知之甚少的人(主要是数据科学家)带入了编程世界,就像计算机科学将一大批人带入了 “工程 “世界一样,尽管他们对计算机知之甚少,更不用说操作系统层面的知识了,这就是为什么像 heroku 这样的 SaaS 试图弥补这一差距,因为这些 “工程师 “不会操作系统!

  32. 又是老生常谈 Python 如何糟糕。
    这篇文章基本上就是一个外科医生在抱怨厨师刀在外科手术中的糟糕表现;或者抱怨 F-16 无法在战场上闲逛并进行扫射;或者抱怨碳纤维管在水下无法承受抗压强度;或者抱怨使用 C 来进行字符串操作和复杂的 regex。
    你用错了工具。

  33. 这个{{insert_programming_tool_here}}对我和我所参与的项目来说效果并不好,所以任何人都不应该使用它。如果你使用了它,你就不是一个真正的程序员,你应该感到羞愧。因为只有使用 {{isnert_programming_tools_i_like}} 的人,才能称得上真正的程序员。这些文章的配方。

  34. > 当然,Go 并不完美(提示:没有一种编程语言是完美的)。
    这是唯一相关的说法。

  35. Python 已经是碳基智能最流行的语言,但现在它也正在成为硅基智能的唯一语言。
    未来,人工智能将使用 Python 编程,而人类程序员则将在博客上写下 Python 有多么糟糕。

  36. 我对 Python 的看法是,它被打上了跨平台的烙印,但最终却要求你学习 docker 并在 linux 环境中运行,这样才能真正摆脱痛苦。d

    1. 在 docker 中运行 Python 3.4 应用程序有什么帮助?Python 3.4 已经过时,而且不会再有安全补丁。在 docker 中运行它不会改变这一点。

  37. 现在是 2023 年,我们有 Ruff 和 Pyright。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注