点击上方 \"程序员小乐\"关注, 星标或置顶一起成长
每天凌晨00点00分, 第一时间与你相约
每日英文
I have passed your heart, not I don\'3 U b ` h * B 2t want to stay, but you would not shelter.
我曾路过你的心,不是我不想d = P z D停留,而是你不肯收留。
每日掏心话Y = # l s B K 4
理想和现实总是有差距的,幸好还有差距,不然,谁还C a m } m b稀罕理想。
来自:公众号 新智元 | 责编:乐乐
程序员小乐(ID:study_tech)第 827 次推文 图片来自百度
往日回顾:Spring 和 Spring Boot 之间到底有啥区别?
正文. h 2
【导读】作为机器学习程序g u 5员的首选编程语言,Python成为世界范围内最受大学生欢迎的编程语言。但凡事有例外,近日( 4 v Q ?,一位开发者讲述了他无法忍受PythE F Y m m x Kon的8大原因,引发网友大量回应。
作为机器学习程序员的首选编程语言,Python近年来可谓如日中天,人气连年暴涨。由于AI热潮持续不断,Python在今年更是取代Java,成为世界范围内最受大学生欢迎的编程语言。h 6 ? l C j很多机器学习领域的教材、文章和技术文档,给出代码时会以Python作为示例语言,可见Python受欢迎程度之高。
Python具有上手快、门槛低、语法结构相对简单等优点,初学者易入门、老手的二次学习成本也低,再加上机器学习任务上优势独具,受热捧简直是水到渠成的事。
但凡事有例外,: C ; a东西再好也不可能人人都爱。
近日在ha] 9 &ckerfactor上,一位名叫NeJ F ! 7 H 3 V @ Ial Krawetz的人就撰文,指0 a u A出了自己无法忍受Python的八大原因,把Python里里外外吐槽了一遍。
文章列出了作者认为Python存在重大+ $ r ^缺陷的八条理: s r : B t h 1由,包括版本兼容性问题、安装版本混乱、在程序关键字命名规则、常用库命名规则上独树一帜,且缺乏一致性、m V }赋值传递混乱、本# Q s c G g 4 b地文件命. , @ Y ~ o &名策略易8 T i J I d出错等。
总之一通下来,把Python贬得够呛。这篇文章在当下Python大热的背景下可算是一朵“奇葩“了P K P。看多了Python赞歌A $ X c h : Y 0 8是不是有点审美疲劳了?不妨换换口味。
原因1:版本之间不兼容
安装Linux后,那么它很可能默认会安装多个版本的Python,可能有Python2和Python3,甚至更多零零碎碎的版本,如3.5或3.7。
这是有原因的:Python3与Python2不完全兼容,一些其他版B - Y 5本在这方面的缺陷也足够明显E 5 K 6 x ,——向后兼容性不足(backwards compatibility,也称为向下兼容性)。
所以Ubuntu同时安装了Python2和Python3,因为这些版本的核心功能是不同的。
缺P X X d 9乏向后兼容和分离h n T h版本通常是走向衰败的预警。Commodore创建了第一台b 1 Q q家用电脑(要远早于IBM PC或Apple之前)。但Commodore PET与随后的Commodore CBM计算机并不兼容,而CBM与VIC-20,Commodore-64,AT d ) l x 9 E `miga等也不兼容。因此,用户要么花费大量时间将代码从一个平台导到另一个平? Q n $ 9台,要么就直接放弃了这个平台——Commodore就是前车之鉴。当用户选择放弃平台时,它? K ^ $ V $ { (就注定会消失。
同样,Perl曾经很受欢迎。但是当Perl3问世时,它并没有完全向后兼容Perl2的代码。k C V 0接下来是Perl4。当Perl5问世时,很多人选择转向使用其他更稳定的编$ R 4 g ; _ * _ 2程语言。所以今天,只有一小部分人还在积极使用Perl来维护现有的Perl项目,而其他0 ; , U N 7 I任何基于Perl的重大新项目再也没有出现过。
同样,Python为每个版本设计了不同的代码孤岛。社区一直拖拽着这些旧版本,所以你最终也只能得到那些旧的、过时的Python代码,因为没有人愿意花时间将它导到最新版本上。
据我所知,没有人在Python2上创建新的代码$ i @ ] 7,但我们还让它苟延残喘着,因为没人将所需的代码导到Python3.x. 在官方Python网站上,这些文档被主动维护并可用于Python 2.7、3.5、3.6和3.7——因为他们无法放弃旧代码。Python就像编程语言的僵尸——行尸走肉般# r - ; & a向前走。
原因2:安装太太太太麻烦了
通常来说,你直接apt、yum、rpm后得到就是最新稳定版。
但你如果\'apt-get install p? ) g x 9 1ython\',就不知道是什么版本,可能与你需要的所有代码都不兼容。所以你在安装的时候需要指定Python版本。
有一个项目需要用Python3.5(当时最新的版本),然+ ? [ f T ) k而我的电脑上最终安装了一大堆版本:Python2、Python2.6、Python3和Python3.5。两个来P ^ 7自操作系统,一个为了项目安装,一7 ] : v q a + | 2个是因为我安装了一些不相关的软件。
尽管它们都是“Python”,但S X D 6 %它们并非完全相同。
如果你想安装Python的软件包,你应该使用“pip”(Pip代表“Pip Installs Packages”)。但是由于系统上有许多版本的Python,你必须记住使用正确版本的pip。否则,\'pip\'可能会运行\'pip2\'而不是你需要的\'pip3x U N ;.7\'。(如果名称不存在,你需要指定pip3.7的实际安装路径。)
我被一个朋友告知我需要配置环境,以便所有东西都能使用Python? | @ 3.5。这种方法的确很有效,但没有持续多久,因为我开始了另一个需要Python 3.6版本的项目。两个并发项目有两个不同版本的Python——emu ( n # x ^mmm,这有n ; l T , & E h P点一言难尽吧。
pip安装程序将文件放在用i * K c户的本地目录中。你不能使用pc x [ip来安装系统范围的库,并且Gawd会阻止你犯下运行\'sudo pip\'的错误,因为这会搞砸整个电脑!
顺便说一句,是谁维w h /护这些pip模块?答案是社区。也就是说,没有明确? = P V S : 5 |的所有者,也没有强制性的责任所- R J a 2 J属。今年早些时候,一个版本的PyPi有一个后门发生了Su T ] j C ~ - )SH凭据盗3 . `窃,但我对此一点都不惊讶,因为社区存储库根本不值得相信。出于同样的原因,我也不使用Node.js和npm。
原因3:令人头疼的语法问题,作用域使用空格导致可读性差
我是可读代码的坚定信徒。乍+ $ $ S t M {一看,Python似乎非常易读,而当你开始制作大型代码库,这种易读性就会减弱了。
其他编程语言,像C, Java,J v # y ? 2 k 1 JavaScript, Perl, and PHP,用{} 来表示作用域;List用()。Python用空格。如果你需要给一个复杂的代码定义 y c 4 A一个作用域,然后你缩进了下面几行代码,当缩进终止后,作用域就终止了。
Python手册说你可以使用任意数量的空格或制表符来定义范围。但是,每次缩进都要用四个空格!如果要缩进两次以进行嵌套,那就得使用八个空格!
Python社区已经对这个术语进行标准化,尽W z r管它没出现在Python手册中。文档I ` A @ +中的示例说可以使用TAB、“TABn N E _+1空格”等等。但是社区却对4个空格有着丧心病狂的偏执!因此,除非你打算永远不向其他任何人展示你写的代码,否则每个缩进都要使用四个空格。
当我第一次看到Python代码时,我认为使用缩进来定义范围似乎是个好主意。事实上,我太天真了,这j ] j简直是一个天大的缺点。
深度嵌套是可以进行的,但每行代码会变得很宽,不得不在文本编辑器中换行。长函数和长条件操作都可能让开始与结束变得难以匹配。我可怜那些错误计算空格数量(比如只输了3个空格而不是四个)的人,因为这样的错误需要数小时[ U q Y q u m l @进行调试和追踪。
我debug代码习惯没有缩进,这样我4 3 L & * } &就可以快速浏览代+ ~ @码,并在完成后轻松识别和删除debug代码。
但是用Python呢?缩进错误的话,都会报错。
原因4:特立独行的加载库方式
大多数编程语言都有一些方法可以包含其他代码块。对于C,它是“#include”。对于PHP,h $ q ; Q r / k S有\'include\~ D , s m 6 & p',\'include_once\',\'require\'和\'r{ ) 3equire_once\) $ i h T'。而对于Python,则是“import”。
Python的import允许导入整个模块、模块的一部分或模块中的特定功能。但查找导入代码块的方法却很麻烦。使用C,直接看/usr/include/*.h就行U S * ( f s P v r了。但用Python?最D M ( I 6好使用\'python -v\'列出所有位置,然后搜索该列表中每个目录和子目录中的每个文件。这X @ Z Z a m Y {真的很麻烦。
导入功能还允许用户重命名导入的代码,它们基本上定义了一个命名空间。乍一看,这似乎很不错,但这最v 0 [ C [ b I ? Z终会影响可读性和长期支持。重命名模块非常适合小脚本,但对于大程序来说真的很糟糕。这样的操作“import numpy as n”S z T h ; } ` d r,应该被打死。
但这不是最糟糕的部分。对于} 5 ! k F大多数语言,包含代码真的只意味着包含代码。而一些语言(如面向对象的C ++)则可以执行代码。类似地,一些PHP代码可能会x ? 6定义全局变量,因此一项i] Y 3 X N mport可以运行代码,但这通常被认为是一种不好的做法。相比之下,许多Python模块包含在导入* v p期间运行的初始化函数。你不知道什么在运行,你不知道它在做什么,你甚至都没察觉到。除非^ / t s存在命名空间冲突,否则在这种情况下,你需要花很长时间来查找原因。
原因5:关键字和库命名^ j /“独树一帜”
在其他所有编程语言中,数组都称为“array”。在Python中,数组被称为“list”。在其他语言中,关联数组有时称为\'hash\'(Perl),e u { ! -但Python里叫做“dictionary”。Pytho6 f I = h / n似乎没有使用在计算机和信息科学领域的常用术语。
然后是库的名称。看看这些名字吧,PyPy、PyPi、Nz q z [ ; ( MumPy、SciPy,SymPy、PyGtk、Pyglet,PyGame ...(是的,6 ` , T S K & l前两个名称发{ / j % E K音一模一样,但是它们的功能和用途$ J ` D O z有很大区别。)我知道“py”代表Python。但这两个字母就不能固定在库的开头或是末尾吗?
而且一些常见的库并没有沿用这个所谓的“Py”命名约定。比如matplotlib、nose、Pillow和SQLAlchemy。虽然从一些命名上能够看出库的一些功能(比如“SQLo q 5 3 | DAlchem3 v W $ 6 k ; 4 #y”包含SQL,所以它可能是一个SQu j ! !L接口),但很多名称只是随机化的单词。如果你事先并不知道“BeautifulSoup”是干什么用的,你能从名称中看出它是一个HTML / XML解析器吗?
(顺便说一句,Beautif] 6 6 j 4 s ! { DulSoup库的说明文档很完备,非常易于使用。如果每个Python模块都这么好用,我也不会L 4 _在这里吐槽这么多。但遗憾的是,这只是B w x个例外,而不是常态。大多数Python库的文档都烂的要死。)
总的来说,我认为Python对库的命名非常混乱,缺乏一致性的原则。我总觉得,开源项目的命名都存在这种规则混乱的问题。除非你了解这个项目,否则你从项目名字上根本看不出来。除非你知道要找的是什么,否则你很可能永远都无法找( a ; { I到, % | X . t ; =想找的东西。从大多数Python库的命名上看,我n d 4 q * ` C |现在更加确信这个观点j B ` ! _了。
原因6:其他“独树一帜”之处略多
每种语言都有它的怪癖。在CQ L f ( N H T M c语言中,使用&和*来访问地址空间和值是奇怪的命名法。C也有“a I r A P y : v++”和 --\"这E ) B样的变量增减控制方式在Bash语` N + ( V言中,当引用括号和正则表达式的句点等特殊字符时,需要使用反斜杠。
JavaScript存在兼容性问题(并非每个浏览器都支持所有有用的功能)。但是,Python比我O G I )见过的任何其他语言的奇怪之处更多。以字符串为例:
•在C中,对字符串使用双引号,对字符使用单引号。
•在PHP和Bash中,两种类型的引号都可以用于字符串。但是,使用双引号时可以在字符串中嵌入变量。相比之下,使用单引号括起来的字符串属于文字。任何类似嵌入式变量的名称都不可扩展。
•在JavaScript中,单引号和双引号之间确实没有区别。
•在Python中,单引号和双引号之间没有区别。但v $ H是,如果想让字符串跨行,则需要使用三引号“”“string”“”或“\'\'string\'\'\'。如果想使用二进制文件,那么你需要用b(b\'binary0 z J - y\')- f U E P v z 或r(r\'raw\')Q 3 @ s 5 L来优先选择字符串。有时还需要使用str(string)进行字符串转换,或使用string.encode(\'utf-8\')将其转换为utf8。
如果x - u g # W =你认为=、d x % w E C==和===这些符号PHP和JavaScript中有点怪,那么等你在Python中` M C使用引号时再说吧。
原因7:赋值方式怪异
大多数编程语言都s o O ~ - a / J A按值传递函S 0 x f 0数参数。如果函数改变了值,则结果不会传递回调用代码。但Python不一样。Python默认使用pass-by-object-reference参数执行函数。这意味着更改源变量可能最终会改变值。
这是面向程序、面向函数和面向对象编程语言之间的重大差异之一。如果每个变量都是通过对象引用传递的,而且对变量的* Z } - Q任何更改都会导致其他所有地方的变量值变化,那么其实也可以全部使用全局变量来处理所有内容。使用不同的名称调用同一个对R n / E , Q 0 1象不会更改对象的值,因此实际R Y ( v r上该对象就是全局的。C语言程序员有句老话,全局变量是邪恶的,不应该使用。
在Python中,必须按值传[ * T J递变量。“a = b”只是为同一个对象空间指定另一个名称,并不会将b的值赋到a中。如果要赋值,则需要使用copy函数。通常格式是“aV F G p = b.copy()”。但是,请注意我说的是“通常”。? s s并非所有数据类型都能够这样赋值,部分功能可能不完整。这时需要使用~ % v一个名为“copy”的独立库:“a = copy.deepcopy(b)”。
原因8:本地程序命名易混乱
根据使用的库或函数来命名程序是一种常见的编程4 w [技术。比如,我正在使用名为“libscreencapture.so”的C语言库测试屏幕捕获程序,我调用的程序可能会命名为“screencaptureO % ; d * C @.c”,编译后命2 w E P 2 * q名为“screencapture.exe”。
如果使用C,Java,JavaScript,Perl,E a 4 o m xPHP等语言,这种命名方式很好用,因为程序语言可以很T s M @ | w容易地将资源库与本地程序区分开来,因为彼此的路径是不[ W 2 J L L W m L同的。但是如果用的是] ) [ } j :Python,永远不要这样命名。
为什么?Python总是假定用户首先要导入本地代码。如果我有一个名为“screencapture.py”的{ ) . e H /程序使用“importscreenc2 r g Q B Tapture”,那么它将导入自己而不是系统库。至少,本地程序需要命名为“myscreencapture.py”才能避免这种错误。
当然了,吐槽了这么 n , ; ` m ) j多,但其实PytS c shon并非一无h + h y是处。
Python是一种非常流行的语言,拥有数量. 3 Q % } L * _庞大的使用者。| _ E我身边有一些朋友非常喜欢PythoX U 4n,这是他们首选的编程语言。多年来,我和他们讨论过这些问题,每次x b 3 S Z P ^ S他们都点头表示同意。他们并不否认PI H ) o O & & 2ythm - : } O J l Aon确实存在这些问题,只是觉得这些缺点不足以让他们抛弃Python。
我, 0 ^的朋友经常在编程中将所有存在的非常酷的Python库统统引用。我也认为一些库确实非常有用。例如,BeautifulSoup是我9 N ^ 7用过的最好的HTML解析器之一,NumPy使得多维数组和复杂的数学y = N 3 Z W o Q m过程更容易实现,而TensorFlow则对于机器学习非常有用。但是,我不会因为喜欢TensorFlow或SciX Q lPy,而在Pythj / non中创建一个单片程序。为了某些库的便利+ W O O | H l f性,放弃程序可读a | j u X w性和可维护性,属于得不 Q #偿失。
一般来说,我在写关于某个主题的负面批评文章时,也会尝试写一些正面的东西。比: O C 5 t如当我写FFmpeg的局限性时,我也b 3 3 +明确提到它是最好的视5 @ & V u频处f ( 2 $ _ A &理库。但f g z Q & #我这里写不出关于Python的什么优点了,因为我真的觉得Python很烂。
此文一发,在评论区引发+ g { S c ) c f z了激烈的争论:
“是你不懂Python”
Mar3 w e * v = G -io Abarca
你的这些问题可以总结为一点:你不喜欢Python因为它和C风格不一样
-
版本不兼容不是bug,是特性;我就觉得没人维护的东西就不应该再用了
-
用虚拟环境安装不同的python版本而不是安装在同一个环境下
-
现代编辑器默认TAB=4个空格。你也不需要非得用4个空格,但要确保一致性
-
官方的文档特别好,真的。要是标准库里没有,翻翻The Hitchhiker’s Guide to Python这本书
-
我觉得Python的命名风格特别好,更直观。list不是数组,就是序列;关联数组明明就是dict
-
二进制字符串前面加个b,是因为Unicode规范中,1字节≠1byte
-
这样做的好处是,我可以随时随地引用一个东西,而不需要每次都去复制粘贴原来的名字
-
同上
notacoward
-
1和2是同一个问题,有关整个生态,跟语言本身无关。因为这类社区维护的项6 ~ q : a目都是不同的人花费宝贵的业余时间去维护,每个人都有每个人的习惯和价值观
-
这个只能说你自己太个性了。我们大家保持默认的统 q % & T一风格,对于别人维护起来明显更容J t 3 g易
-
C/C++的include很难处理模块接口
-
lis- 3 R ^ n F ` ! kt和array不是一个东西。下一个
-
每个语言都有自己的一套转换方式。Python可能不是最完美的 d D :,但是其他的更差,呵呵
-
对象引用效率更高。尤其是当变量名不一致的时候,你直接复制会有问( ^ %题。但是你引用一下,就好多了
-
最好不要把自己的程序命名成标准库里的程序或者模块的名字
folkrav
首先要纠正下你,P= g c b 9 O SyPy和PyPi发音不一样。前一个是“派派”,后一个是“派-屁-爱”
其次,名称很重要吗?第三方诶大哥,啥名字都可以出现诶大哥。你就能保证你起名的时候,能做到信达雅吗大哥?r j s @ % L
jaxtellerSoA
我就不明白了。用缩进来定义作用域,怎么就不好了?多一目了然啊S e u [ )!别的语言{}里面不也得缩进吗?再说了,你就不觉得按住shift才能打出{}很难受吗?
riskable
我跟C粉儿讨论过“缩进vs括号; k 4 0 K ) t m @”这个问题。他说没有括号怎么能轻松找出作用域呢?I U %
标准Py粉儿答案是:啊原来你们喜欢括号是v L _ P 8因为你们的代码坏习惯啊。
我想了想,可能这么问更恰当:假如不使用文本编辑器/IDE来突出显示括号或它们之间的空间,D d w你还是坚持用括号不用缩进吗?
我估计他终于get到我的点了,说:啊我明白了,你之所以用缩进是因为Pyt6 + /hon编辑器太烂了啊!真可怜。
Sign。
“我也不喜欢# d F / k ! A `Python”
cutety
Python是我上手的第一个语言,但我以后再也不会用了。当然作者的这些问题,在我看来都不是问题,个S 8 6 人习惯而已。
-
包管理模式简直烂到家。那么多包管理器可以借鉴啊,可以让pip不那么烂啊
-
就不能有个标准包管理器有个标准manifest吗?又不会怀孕!
twunds , i j b ^ k s Ze
安装确实是个让人头疼的问题。是的很多人提到了安装虚拟环( ^ e境,venv/virtualenv。Ruby有* O X l f kRVM,可以轻松的在同样环境下使用不同版本。我m 2 & n宁愿挨个给Ruby,PHP,Perl 4 ( A…做环境配置,也不愿意给Python配置。
nicoburns
哦!多行Lambda!我在JS里的最爱。Python里,没!J J 0 : ? N h i [有!了!
setpatcha w x |address
我从1.5就开始用Python了。缩进来表示语句块,是我最最最不能忍受的!
dbcurtis
我就喜欢C那种的括号,不喜欢Python的缩进方式。
colae a - V @ m ^ E +nderman
我觉得Python最大的问题其实是内部模型对于它的意图而言过于复杂了,就是一个有经验z T E的开发者都很难理解,别说初学者了。
你怎么看?
参考链接:
hackerfactor.com/blS B N M _og/index.php?/ar[ } k p B ! bchives/8X W = v Y 625-8-Reasons-Pythot E K 2n-Suckn L i Os.html
news.ycombinator.com/item?id=18706174
欢迎在留言区留下你的观点,一起讨论提高。如果今天的文章让你有新的启发,学习能力的提升上有新的认识,A t o欢迎转发分享给更多人。
猜你还想看
阿里、腾讯、百度、华为、京东最新面试题汇集
Java之异常处理,看这篇就对了!
Java IO使用的四种模式
(三)SpringBoot+SpringCloud —— 高可用的Eureka注册中心
关注订阅号「程序员小乐」,收看更多精彩内容
嘿,你在看吗?