作者:Phillip J. Eby.
翻译:ShiningRay @ NirvanaStudio
原文地址:[[http://dirtsimple.org/2004/12/python-is-not-java.html][1]][1]
我最近正在看一个基于wxPython的GUI应用程序,大概45.5KLOC的样子,但我没有计算它用到的库的大小(如Twisted)。代码是由那些对Python相对生疏的Java的开发者写的,所以程序有很严重的性能问题(如三十秒的启动时间)。我在检查代码的时候发现他们写了很多对Java有意义但是对Python却很恐怖的东西。并不是因为“Python比Java慢”,而是因为在Python中有更方便的方法去完成同样的目标,甚至在Java中不可能的事情。
所以,可悲的事就是这些可怜人事倍功半,产生了很多很多不需要写的代码,从而比相应合乎Python习惯的写法慢得多得多。我们来看一些例子:
* 在Java中一个静态的方法(static)不能翻译成一个Python的类方法(classmethod)。哦,当然,多多少少他最终产生类似的效果,但类方法的目的实际上是做了一些通常在Java中不可能的事(如继承一个非默认的构造函数)。Java静态方法的习惯翻译通常是一个模块级函数,而不是一个类方法或静态方法(staticmetho)。(同时静态封闭(final)字段应该翻译成模块级常量。)
这并不是一个性能上的问题,但是一个Python程序员要用像这些类似Java习惯的代码的话,可能就会被在该输入Foo.someFunction时却要输入Foo.Foo.someMethod这种情况给惹毛了。但是请注意:调用一个类方法将会比调用一个静态方法和函数要多一部分额外的内存。
啊,那些Foo.Bar.Baz也不是省油的。在Java中,这些点分割的名称是由编译器去查找的,所以运行时根本无所谓你有多少点。在Python中,每次运行时都要查找,所以每个点都要计算在内。(Python中一定要记住这点,“平铺比嵌套好”,尽管比起性能,他和“可读性”和“简单就是美”更靠近。)
* 要用switch语句?Python翻译将是一个哈希表,不是一堆if-then语句。用一堆if-then在Java中也不是switch语句,如果有字符串参与了呢?他其实是一个哈希表。CPython字典实现用了性能最佳??在我们宇宙中目前所知道的?的哈希表的实现之一。你自己所写的代码也不会比这个再好了,除非你是Guido、Tim Peters和Raymond Hettinger的关门弟子。
* XML不是答案。它也不是一个问题。要在正则表达式上解释Jamie Zawinski,“一些人,当遇到一个问题的时候,就想‘我知道,我要用XML’那这个时候,他们就有两个问题了。”
和Java比这个一个不同的情况,因为比起Java代码,XML是轻巧而且有弹性的。但比起Python的代码来,XML就是一个船锚,一个绊脚石。在Python中,XML是用来做交换,而不是你的核心功能,因为你不需要这么做。在Java中,XML可能是你的大救星因为他让你实现了特定领域的语言并“不通过编码”提高了你的应用程序的适应性。在Java中,避免编码是一个很大的优势,因为编码意味着重新编译。但在Python中,更常见的是,写代码比写XML更方便简单。同时Python处理代码要远远比处理XML快。(不仅仅是这个,你必须书XML处理代码,同时Python自身就已经为你准备好了。)
如果你是一个Java程序员,对于你是否要在你的Python核心应用中使用XML作为一部分,不要相信你的本能。如果你不是因为信息交互的原因去实现一个已经存在的XML标准或是建立某种导入、导出格式或者建立某种XML编辑器或处理工具,那么就不要这么做。一次也别。甚至连想都不要想。现在,扔掉那个XML模式把你的手解放吧!如果你的应用程序或者平台要被Python开发者使用,他们只会感谢你不要在他们的工作量中添加使用XML的负担。
(这里唯一的例外是如果你的受众的的确确,确确实实需要XML,出于某种奇怪的理由。像,他们拒绝学习Python并只对你使用了XML而付钱给你,或者你打算给他们一个编辑XML的GUI,同时这个写XML的GUI呢是另一个人写的,同时你得到免费使用的权利。还有一些很少见的架构上的原因需要用到XML。相信我,他们不会出现在你的程序中。如果有疑问,对一个资深的Python开发员解释你的用例。或者,如果你脸皮厚的话,试试向一个Lisp程序解释你的程序为什么要用XML!)
* Getter和Setter是坏蛋!坏蛋!魔鬼! Python对象不是Java Bean。不要写什么getter和setter,然后还把它们包装在“属性”里面。它直到你能证明你需要比一个简单访问复杂一点的功能时才有意义,否则,不要写getter和setter。它们是CPU时间的浪费,更要紧的是,它们还是程序员宝贵时间的极大浪费。不仅仅对于写代码和测试的人,对于那些要阅读和理解它们的人也是。
在Java中,你必须使用getter和setter因为公共字段不允许你以后改变想法再去使用getter和setter。在Python中,这样做很傻,因为你可以以一个普通特性开始并可以在任何时间改变你的想法,而不用影响到这个类的任何客户。所以不要写getter和setter。
* 代码重复在Java中常常是一个不得不要的魔鬼,你必须经常一遍一遍写同一个方法而只有一点点的变化(通常是因为静态类型约束)。在Python中这样做是没有必要的也是不值得的(除了极少数一些特定的场合需要内联一些要求性能的函数)。如果你发现自己一遍一遍在写同样的代码而且变化很少,你就需要去学一下闭包。他们并不是真的很可怕。
这就是你要做的。你写了一个包含了函数的函数。这里内部的函数就是你要一遍遍写的函数的模版,但是在里面加入了针对不同情况的函数要使用变量。外部的函数需要刚刚提高的那种变量作为参数,并且将内部的函数作为结果返回。然后,每次你要写另一种略微不同的函数的时候,你只要调用这个外部的函数,并且把返回值赋给你要让“重复”函数出现的名字。现在,如果你需要改变这个工作方式,你只要改变一个地方:这个模版。
在我所看过的应用程序/平台中,只有一个很微不足道的程序使用了这个技术之后可以去掉数百行重复代码。事实上,自从开发者使用了特别的样板文件来为这平台开发插件,这会节省很多很多第三方开发人员的代码,同时也使那些程序员要学习的东西简化了。
这只是Java->Python思维方式转变的冰山一角而已,现在我可以让他转变成正确的而不用钻研这个程序的细节。本质上,如果你曾经用过一段时间Java,而且对Python比较陌生,不要太相信自己的本能。你的本能已经为Java调节,而不是Python。向后退一步,最重要的,不要写这么多代码了。
要这样做,让自己觉得更加需要Python。假装好像Python是可以做任何你想做的魔棒,却让你无须动一个手指。问一下,“Python是怎样解决我的问题的?”还有“Python语言的哪个特点和我的问题最相似?”你绝对会惊讶于你需要的东西其实已经有了某种固定形式。事实上,这种现象实在是太普遍了,甚至在很有经验的Python程序员中也会出现,以至于Python社区中给这种现象起了个名字。我们称之为“GUIDO的时间机器”(GUIDO是美语中太空飞行工程师的意思),因为有时候看上去得到我们所需要的东西好像只有他知道的一种方法,但当我们自己知道了就不一样了。
所以,如果你不能感到你在使用Python时至少比用Java要多出10倍的生产力,!(同时如果你还怀念你的Java IDE,考虑一下这种可能性:因为你写的Python程序比他所需要的要复杂得多)
附录:(翻译自此篇文章的评论)
确实,哈希表==字典。举个最简单的例子,从Python标准库中检出“pickle”和“copy”模块,这两个模块会从字典中查找类型并调用相应的函数。另一个有些诡异的例子是范型函数,我已经在最近的Blog中写了一下。
关于闭包的例子,我这里给出一个很笨的资历。假设你要写很多这样的函数:
def addOne(x): return x+1
def addTwo(x): return x+2
然后你可以这样写:
def makeAdder(addend):
... def add_it(x): return x+addend
... return add_it
并且这样使用:
addOne = makeAdder(1)
addTwo = makeAdder(2)
这样就可以等同于原来的定义了。
[1]: [http://dirtsimple.org/2004/12/python-is-not-java.html][2]
[1]: http://dirtsimple.org/2004/12/python-is-not-java.html][1]
[2]: http://dirtsimple.org/2004/12/python-is-not-java.html