关于“Python不是Java”的问题
我是一名初学者,刚学了一些Java,现在正在学习Python。最近我在另一个问题的讨论中看到了一篇博客文章:
http://dirtsimple.org/2004/12/python-is-not-java.html
我对这篇文章提到的一些内容有几个问题:
1) “哦,那些 Foo.Bar.Baz 这样的属性链可不是免费的……所以每个点都很重要。”
解决这个问题的方法是提前导入模块和它的方法吗?比如说:
from Foo.Bar import Baz
...
#now Baz() can be called directly without using Foo.Bar.Baz() everytime
2) 有 switch 语句吗?Python 的对应方式是哈希表,而不是一堆 if-then 语句。
关于这个话题有几个相关的回答,但它们也引出了几个问题:
- 使用 if-else 更简洁,但它没有 switch 语句那样的常数时间 O(1) 的优势。
- 使用哈希表可以实现常数时间 O(1)。
- 在哈希表中使用 lambda 函数进行比较(不推荐)。
- 为什么不推荐?是因为 lambda 函数会影响哈希的常数因子吗?
- 使用 bisect 模块。
- 这种方法能保持常数时间 O(1) 吗?还是说它只是另一种 lambda 函数?
- 那么在 Python 中,有什么方法可以等同于 switch 语句,并且能保持常数时间 O(1),同时又允许比较语句?
3) Getter 和 setter 是邪恶的。邪恶,邪恶……不要写 getter 和 setter……这就是内置的 'property' 的用处……在 Python 中,使用 getter 和 setter 是傻事,因为你可以从一个普通属性开始,随时改变主意,而不会影响到类的任何使用者。
我对这一部分不是很理解。
而且,在 Python 中,公共和私有的方法或变量可以很容易地被访问,这与 C++ 和 Java 的情况正好相反。这种行为有什么设计上的原因吗?
最后,有没有推荐的关于 Python 和其他编程语言比较的好书或文章?
3 个回答
也许吧。并不是总能以这种方式进行特定的导入。
一般情况下,不太建议使用 Lambda 函数,原因有很多(并不是我都同意)。bisect 是一种二分查找,所以它的复杂度是 O(log N)。
通常,解决这个问题的方法是使用本地名称(变量),这些名称指向特定的对象实例(比如在你的循环范围内)。Python的动态语义要求解释器每次都要检查每个“.”引用,因为这些引用所指向的对象可能在循环的前几次迭代中发生了变化。关于性能的讨论,大多数情况下在小规模操作时影响不大,但如果你在处理大规模数据(比如几百万次循环,每次循环中还有多个“.”引用)时,这种影响就会显现出来。
有时候,更好的方法是重新考虑你的设计,使用“更聪明”的对象。很多时候,程序员会使用switch/case语句在一个对象类中实现不同的行为,但其实可以创建多个相关的类,每个类根据自己的类型“做正确的事”。另外,有时你可以创建一个字典(“哈希表”),用来存储键和对象(函数、实例、匿名函数等),并将其用作调度表。
Python允许你直接访问属性(不需要使用getter/setter方法),如果需要的话,你可以使用属性来确保你的代码(getter/setter)被隐式执行。使用你代码的人不需要知道或关心
foo.bar=1
是直接给foo
的bar
属性赋值,还是调用了foo
的某个方法,这个方法可能会进行一些内部状态的操作,后续访问foo.bar
时会看到这些变化。这些都是实现细节。语言鼓励程序员将所有对象/属性引用都实现为显式的“.get()”和“.set()”调用,同时又支持更简单的“.”语法,这对代码没有好处,只会让代码变得更加杂乱,难以阅读。
是的,属性和方法可以很容易地访问,且“隐藏”它们是很困难的。这让语言更容易使用。(反对的观点是,这让类更容易被“滥用”——也就是说,允许用户访问类的实现细节,而这些细节并不是公开的……这并没有强制他们尊重预期的抽象。这是个无关紧要的问题。如果你的类文档清楚地说明了预期的接口,并合理地实现了这些接口,那么大多数程序员会使用这些接口。如果他们觉得需要去干扰你的实现细节,那可能是你做错了——而他们的替代方案就是重新实现你所做的事情,以绕过你施加的限制。
- 这事儿一般不太重要。如果真的重要,那你最好用C语言写代码(或者让Cython帮你处理,配上静态类型注释,这样效果更好),或者使用PyPy(它的JIT技术可以在某些循环中完全消除这种查找,甚至内存分配等问题)。
- “lambda”跟“if-elif链”或者“bisect”是不同的概念。你具体指的是哪种技术呢?很多人不喜欢
lambda
,因为他们觉得它相对冗长或者不容易读懂(至少它的某些用法很快就会变得这样)。bisect
只是维护一个有序列表,所以你能得到的最佳查找效率是O(log N)的二分查找。想要一个switch语句?用字典就行。比较操作(当然除了==
)不在switch
的范围内,而且大多数常见的比较方式都让O(1)的查找变得不可能。 - 有什么地方让你觉得难以理解吗?在写Python时,你不需要写getter或setter方法。你直接用普通的属性。如果后来发现需要在访问或修改属性时加点逻辑,你可以把它变成一个属性,这样所有使用它的代码都可以继续运行,不用修改。
- 至于缺乏真正的隐私——这个问题已经问过很多次了,简单来说就是“我们都是成年人”,也就是说,程序员被信任不会随便动私密的东西,除非有很好的理由。另一个稍微弱一点的理由是,在动态语言中实现这一点比较困难,至少在目前的对象模型下(比如方法只是普通函数)。