作为一名学习Python的Java程序员,我应该注意什么?

63

我的编程背景大部分是Java,并且我仍然在大部分时间里使用Java进行编程。但是,我开始在工作中的一些副项目中学习Python,并且我希望尽可能独立于我的Java背景来学习它 - 也就是说,我不想只是用Python编写Java。我应该注意哪些问题呢?

一个快速的例子 - 在查看Python教程时,我发现函数的默认可变参数(例如列表)被保留并传递给下次调用。这对我作为Java程序员来说很反直觉,很难理解。(如果您不了解示例,请参见这里这里)。

有人还向我提供了这个清单,我觉得很有帮助,但很简短。还有没有其他例子可以说明Java程序员可能会误用Python...?或者说Java程序员会错误地假设或难以理解的事情呢?

编辑:好的,根据Bill the Lizard的建议,我将简要概述文章中提到的原因以避免重复。(如果我用词不当,请告诉我,我刚开始学习Python,可能不完全理解所有概念。还有一个免责声明 - 这些内容会很简单,如果您不了解它的含义,请查看链接。)

  • Java中的静态方法不能直接翻译成Python中的类方法
  • Java中的switch语句在Python中可以用哈希表来代替
  • 不要使用XML
  • Getter和Setter是邪恶的(嘿,我只是引用 :) )
  • 代码复制在Java中通常是必要的恶(例如方法重载),但在Python中不是

(如果您认为这个问题有趣,无论如何都请查看链接。 :) 它非常不错。)


2
好问题。你在最后一段链接的文章是一个非常好的起点。你可以考虑提取出那里给出的要点的(非常简短的)摘要,这样人们就不会在回答中重复相同的列表了。(我差点就发了文章中的一个要点。) - Bill the Lizard
@Bill the Lizard - 已更新,感谢您的建议。只是出于好奇 - 您要发布哪个观点? :) - froadie
一种飞行的感觉? - Alex Miller
1
此外,请将其设为社区维基。这里没有“答案”,只有许多建议。 - S.Lott
1
相关:http://stackoverflow.com/questions/954164/choosing-between-java-and-python - S.Lott
1
我本来想发帖说“Getter和Setter是邪恶的”。在这一点上,两种语言之间有着天壤之别。它们很可能是最容易识别出只是用Python编程的Java程序员的方法。 - Bill the Lizard
7个回答

25
  • 不要把所有东西都放进类中。Python内置的列表和字典会让你走得更远。
  • 不必担心每个模块只有一个类。按目的划分模块,而非按类划分。
  • 使用继承来实现行为,而不是接口。不要创建“动物”类供“狗”和“猫”继承,只是为了获得通用的“发声”方法。

就这样做:

class Dog(object):
    def make_sound(self):
        return "woof!"

class Cat(object):
    def make_sound(self):
        return "meow!"

class LolCat(object):
    def make_sound(self):
        return "i can has cheezburger?"

6
我知道我可能忽略了一些简单的东西......但您能否更详细地解释一下您最后一个观点背后的原因? - froadie
4
@froadie说:拥有一个Animal类并不是坏事,只是除非你想提供make_sound()的默认实现,否则完全没有必要。 - dsimcha
11
Python使用“鸭子类型”的概念(或者在这个例子中可以说是狗/猫类型🐶🐱)。如果你向一个期望拥有某种接口的方法传递了一个对象,只要它有这个接口,那么就没有问题。由于继承具有极其紧密的耦合性,所以存在很多问题,因此只有在真正需要时才使用它。 - Ryan Ginstrom
5
多态在动态语言中是隐含的。继承只是告诉静态编译器类型相关的最简单方法。对于动态语言和廉价异常,大多数情况下并不需要使用继承。 - Javier
3
@froadie说:在Python学到的任何东西都不能“反弹”到Java中。它们是不同的语言。这不仅仅是语法不同,而且底层概念也不同。Java的静态声明特性使其与Python非常不同。 - S.Lott
显示剩余2条评论

23
参考文章提供了一些好的建议,但很容易被误传和误解。也有一些坏的建议。
放弃Java,从头开始。"不要相信你的[基于Java的]直觉"。在任何编程领域中说某些事情是"违反直觉的"是一个坏习惯。学习新语言时,从头开始,放弃你的习惯。你的直觉一定是错误的。
语言是不同的。否则,它们将是相同的语言,只是有不同的语法,那么就会有简单的翻译器。因为没有简单的翻译器,所以没有简单的映射。这意味着直觉是无用和危险的。
  • "Java中的静态方法不能转换为Python中的类方法。"这种情况非常有限和不实用。Python有一个staticmethod装饰器。它还有一个classmethod装饰器,Java没有相应的装饰器。

    顺便说一下,这个点也包括了更有帮助的建议,即不要不必要地将所有内容都包装在一个类中。"Java静态方法的惯用翻译通常是模块级函数"。

  • Java中的switch语句可以有多种实现方式。首先,它通常是一个if elif elif elif结构。在这方面,这篇文章并没有提供太多帮助。如果你绝对确定这太慢了(并且能够证明它),你可以使用Python字典作为从值到代码块的略微更快的映射。盲目地将switch翻译为字典(而不加思考)是非常糟糕的建议。

  • 不要使用XML。如果没有上下文,这是没有意义的。在上下文中,它意味着不要依赖XML来增加灵活性。Java依赖于在XML中描述东西;例如,WSDL文件重复了从检查代码中明显的信息。Python依靠内省而不是在XML中重申所有内容。

    但是,Python有出色的XML处理库。有几个。

  • 在Python中,getter和setter不像在Java中那样是必需的。首先,在Python中具有更好的内省功能,因此您不需要getter和setter来帮助创建动态bean对象。(对于这一点,您可以使用collections.namedtuple)。

    然而,您有property装饰器,它将getter(和setter)捆绑成类似属性的结构。重点是Python更喜欢裸属性;必要时,我们可以捆绑getter和setter,看起来就像有一个简单的属性。

    此外,如果属性不够复杂,Python还有描述符类。

  • 在Java中,代码重复通常是一种必要的恶(例如,方法重载),但在Python中不是这样。正确。Python使用可选参数来代替方法重载。

    这个要点继续谈论闭包;这不如明智地使用默认参数值的简单建议有用。


虽然我并不完全同意这里提到的所有事情,但是关于“namedtuple”的建议非常有帮助,你赢得了我的点赞。 - WestCoastProjects
@S.Lott 我想要重新开始,但我真的不想再学习像字符串、整数等基础知识。你有没有什么建议,可以推荐一些书籍或网站给那些已经有编程经验,但想从头开始学习Python的人呢? - Honinbo Shusaku

14

在Java中你可能习惯了严格的隐私控制,但在Python中是找不到这个的。这并不是需要特别注意的问题,反而是不需要寻找的东西(我刚开始学Python时为寻找Python等价于“private”花费了很长时间感到非常尴尬!)。相反地,Python比Java更加透明易于内省。这归结于有时被描述为“我们都是自愿成年人”的哲学。有一些约定和语言机制可以帮助防止意外使用“未公开”的方法等,并且整个信息隐藏的思想在Python中几乎不存在。


6
基本上,当比较Java和Python时,一个经常出现的主题是Python更加信任你——它给了你更多的权力,但也给你犯错误的更大可能性。 - froadie
搜索方面我也有同感。在我的头几天里,我花了很多时间寻找一个私人等效物。 - Zach Varberg
3
Python 的口号是 "我们都是成年人"。Python 是作为源代码分发的。当你可以阅读源代码时,“私有”一词意味着什么?如果必要,你只需编辑源代码以更改内容。 - S.Lott
我认为如果你不了解惯例,这种哲学实际上是有害的。我说惯例,因为语言支持方面很少,所以你必须四处搜索。我看过很多Python代码,其中一切都是公开的,因此很难知道哪些方法是可以调用的,哪些属性是安全可改变的。单下划线约定(self._blah_method())是更好的选择。它并不强制执行隐私,但它确实表明“除非你知道自己在做什么,否则不应该调用此方法”。第二个选项是双下划线。 - Mark Simpson
2
我认为“我们都是成年人”的说法是一把双刃剑。是的,它使挖掘和查找/修改现有代码变得更容易,但缺点是它会产生噪音。如果我下载一个包并想要使用它,除非包作者遵循了可选的约定,否则我将被呈现出许多(无效)选项。在其他语言中,与我无关的部分被封装起来。我只看到类的公共表面。并不是说Python做某些不同的事情就很糟糕,只是这是一个带有缺点的选择。 - Mark Simpson

9
我能想到的最大问题是不理解或未完全利用鸭子类型。在Java中,你需要提前指定非常明确和详细的类型信息。而在Python中,类型既是动态的,又是大部分隐含的。其哲学是你应该以比名义类型更高的层次来考虑程序。例如,在Python中,你不使用继承来模拟可替换性,可替换性默认是通过鸭子类型实现的。继承仅仅是一种方便程序员重用实现的方法。
同样地,Pythonic的语言习惯是“宁愿请求原谅,也不要事先征求许可”。显式类型被认为是有害的。不要事先检查参数是否是某种类型,只需尝试对参数进行所需的操作。如果它不符合正确的接口,它会抛出一个非常清晰的异常,你将能够快速找到问题所在。如果某人传递了一个名义上意外但与预期相同接口的类型参数,则可以免费获得灵活性。

哇,我觉得我不是很理解第一段。我猜我得谷歌一些这些术语,研究Python如何使用它们 :) 谢谢 - froadie
好的 - 我正在研究,发现它将我推向与Ryan的第三点类似的方向。对于有Python背景的人来说,这可能是一个非常简单的问题 - 但是多态在Python中扮演什么角色? - froadie
1
Python具有无限的多态性。只要给定的对象在运行时,在那个代码行中具有所需的字段或方法,那么代码就会运行。换句话说,该对象甚至不需要事先声明该字段或方法,但如果您愿意,可以稍后将其添加到对象中。多态性不依赖于继承,因为鸭子类型完全绕过了编译时的要求。 - Caleb Hattingh
我想我是一个比较明确的人。编译和祈祷并不是我的风格。Python 只是为了懒惰而向后迈进了一大步。 - Damien

7
从Java的角度来看,最重要的是并不是为每个东西都制作类。在许多情况下,过程式方法更简单、更短。
接下来最重要的是,你需要克服这样一个观念:对象的类型控制它可以做什么;相反,代码控制对象必须能够在运行时支持的内容(这是由于鸭子类型的特性)。
另外,尽可能使用本地列表和字典(而不是自定义后代)。

6
Python 中异常的处理方式与 Java 不同。在 Java 中,建议只在异常情况下使用异常,而 Python 则不是如此。
比如在 Python 中,像迭代器等使用异常机制来表明没有更多的项目了。但在 Java 中,这样的设计被认为不是好的做法。
正如 Alex Martelli 在他的书《Python in a Nutshell》中所说,使用其他语言(适用于 Java)的异常机制是 LBYL(先检查再跳,Look Before You Leap):在尝试操作之前,需要提前检查可能使操作无效的所有情况。
而对于 Python,则采用 EAFP 的方法(易于请求宽恕,不易于获得许可)。

1
与“不要为所有事情使用类”相对应的是回调函数。
在Java中,执行回调的方式依赖于传递实现回调接口的对象(例如具有其actionPerformed()方法的ActionListener)。在Python中不需要这样做,您可以直接传递方法甚至是本地定义的函数:
def handler():
   print("click!")
button.onclick(handler)

甚至可以使用lambda表达式:

button.onclick(lambda: print("click!\n")) 

2
-1 - 糟糕的lambda示例。 lambda旨在包含单个表达式,而不是语句,并返回该表达式的值。您的lambda示例虽然可以工作,但实际上返回调用print(我认为是None)的返回值。如果您有一个需要打印2个语句的lambda,您会告诉新手编写lambda:print(“第一行”)或print(“第二行”)吗? - PaulMcG

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接