Python Is Not Java

本文探讨了从Java迁移至Python时常见的误区,例如不恰当地使用类方法、静态方法、getter和setter,以及过度依赖XML等问题。文章强调Python拥有更简洁高效的编程方式,并提供了改进代码质量的具体建议。

Python Is Not Java

 

I was recently looking at the source of a wxPython-based GUI application, about 45.5KLOC in size, not counting the libraries used (e.g. Twisted). The code was written by Java developers who are relatively new to Python, and it suffers from some performance issues (like a 30-second startup time). In examining the code, I found that they had done lots of things that make sense in Java, but which suck terribly in Python. Not because "Python is slower than Java", but because there are easier ways to accomplish the same goals in Python, that wouldn't even be possible in Java.

So, the sad thing is that these poor folks worked much, much harder than they needed to, in order to produce much more code than they needed to write, that then performs much more slowly than the equivalent idiomatic Python would. Some examples:
  • A static method in Java does not translate to a Python classmethod. Oh sure, it results in more or less the same effect, but the goal of a classmethod is actually to do something that's usually not even possible in Java (like inheriting a non-default constructor). The idiomatic translation of a Java static method is usually a module-level function, not a classmethod or staticmethod. (And static final fields should translate to module-level constants.)

    This isn't much of a performance issue, but a Python programmer who has to work with Java-idiom code like this will be rather irritated by typing Foo.Foo.someMethod when it should just be Foo.someFunction. But do note that calling a classmethod involves an additional memory allocation that calling a staticmethod or function does not.

    Oh, and all those Foo.Bar.Baz attribute chains don't come for free, either. In Java, those dotted names are looked up by the compiler, so at runtime it really doesn't matter how many of them you have. In Python, the lookups occur at runtime, so each dot counts. (Remember that in Python, "Flat is better than nested", although it's more related to "Readability counts" and "Simple is better than complex," than to being about performance.)

  • Got a switch statement? The Python translation is a hash table, not a bunch of if-then statments. Got a bunch of if-then's that wouldn't be a switch statement in Java because strings are involved? It's still a hash table. The CPython dictionary implementation uses one of the most highly-tuned hashtable implementations in the known universe. No code that you write yourself is going to work better, unless you're the genetically-enhanced love child of Guido, Tim Peters, and Raymond Hettinger.

  • XML is not the answer. It is not even the question. To paraphrase Jamie Zawinski on regular expressions, "Some people, when confronted with a problem, think "I know, I'll use XML." Now they have two problems."

    This is a different situation than in Java, because compared to Java code, XML is agile and flexible. Compared to Python code, XML is a boat anchor, a ball and chain. In Python, XML is something you use for interoperability, not your core functionality, because you simply don't need it for that. In Java, XML can be your savior because it lets you implement domain-specific languages and increase the flexibility of your application "without coding". In Java, avoiding coding is an advantage because coding means recompiling. But in Python, more often than not, code is easier to write than XML. And Python can process code much, much faster than your code can process XML. (Not only that, but you have to write the XML processing code, whereas Python itself is already written for you.)

    If you are a Java programmer, do not trust your instincts regarding whether you should use XML as part of your core application in Python. If you're not implementing an existing XML standard for interoperability reasons, creating some kind of import/export format, or creating some kind of XML editor or processing tool, then Just Don't Do It. At all. Ever. Not even just this once. Don't even think about it. Drop that schema and put your hands in the air, now! If your application or platform will be used by Python developers, they will only thank you for not adding the burden of using XML to their workload.

    (The only exception to this is if your target audience really really needs XML for some strange reason. Like, they refuse to learn Python and will only pay you if you use XML, or if you plan to give them a nice GUI for editing the XML, and the GUI in question is something that somebody else wrote for editing XML and you get to use it for free. There are also other, very rare, architectural reasons to need XML. Trust me, they don't apply to your app. If in doubt, explain your use case for XML to an experienced Python developer. Or, if you have a thick skin and don't mind being laughed at, try explaining to a Lisp programmer why your application needs XML!)

  • Getters and setters are evil. Evil, evil, I say! Python objects are not Java beans. Do not write getters and setters. This is what the 'property' built-in is for. And do not take that to mean that you should write getters and setters, and then wrap them in 'property'. That means that until you prove that you need anything more than a simple attribute access, don't write getters and setters. They are a waste of CPU time, but more important, they are a waste of programmer time. Not just for the people writing the code and tests, but for the people who have to read and understand them as well.

    In Java, you have to use getters and setters because using public fields gives you no opportunity to go back and change your mind later to using getters and setters. So in Java, you might as well get the chore out of the way up front. In Python, this is silly, because you can start with a normal attribute and change your mind at any time, without affecting any clients of the class. So, don't write getters and setters.

  • Code duplication is quite often a necessary evil in Java, where you must often write the same method over and over with minor variations (usually because of static typing constraints). It is not necessary or desirable to do this in Python (except in certain rare cases of inlining a few performance-critical functions). If you find yourself writing the same function over and over again with minor variations, it's time to learn about closures. They're really not that scary.

    Here's what you do. You write a function that contains a function. The inner function is a template for the functions that you're writing over and over again, but with variables in it for all the things that vary from one case of the function to the next. The outer function takes parameters that have the same names as those variables, and returns the inner function. Then, every place where you'd otherwise be writing yet another function, simply call the outer function, and assign the return value to the name you want the "duplicated" function to appear. Now, if you need to change how the pattern works, you only have to change it in one place: the template.

    In the application/platform I looked at, just one highly trivial application of this technique could have cut out hundreds of lines of deadweight code. Actually, since the particular boilerplate has to be used by developers developing plugins for the platform, it will save many, many more hundreds of lines of third-party developer code, while simplifying what those developers have to learn.
This is only the tip of the iceberg for Java->Python mindset migration, and about all I can get into right now without delving into an application's specifics. Essentially, if you've been using Java for a while and are new to Python, do not trust your instincts. Your instincts are tuned to Java, not Python. Take a step back, and above all, stop writing so much code.

To do this, become more demanding of Python. Pretend that Python is a magic wand that will miraculously do whatever you want without you needing to lifting a finger. Ask, "how does Python already solve my problem?" and "What Python language feature most resembles my problem?" You will be absolutely astonished at how often it happens that thing you need is already there in some form. In fact, this phenomenon is so common, even among experienced Python programmers, that the Python community has a name for it. We call it "Guido's time machine", because sometimes it seems as though that's the only way he could've known what we needed, before we knew it ourselves.

So, if you don't feel like you're at least ten times more productive with Python than Java, chances are good that you've been forgetting to use the time machine! (And if you miss your Java IDE, consider the possibility that it's because your Python program is much more complex than it needs to be.)

 from http://dirtsimple.org/2004/12/python-is-not-java.html
<think>我们在处理用户的问题,用户在使用Python的JPype库时遇到类未找到(ClassNotFound)异常。我们需要解决这个问题。参考引用:引用[1]提到的是关于JPeerGen工具,用于生成C++代码与Java交互,但用户的问题是关于Python的JPype库。引用[2]是关于MySQL的配置,与当前问题无关。因此,我们忽略提供的引用,专注于用户的问题。用户遇到的JPype中的ClassNotFoundException,通常是由于Java类路径(classpath)没有正确设置导致的。JPype是一个Python库,允许在Python中调用Java代码,因此它需要知道Java类的位置。解决方案步骤:1.确保你的Java类文件(.class或包含在.jar文件中)在某个目录中。2.在启动JPype时,通过设置`jvmpath`(如果需要指定JVM路径)和`classpath`参数来指定类路径。例如,假设我们有一个Java类`com.example.MyClass`,它的.class文件位于`/path/to/classes`目录下,那么我们应该这样设置:```pythonimportjpype#启动JVM,指定类路径jpype.startJVM(jpype.getDefaultJVMPath(),"-ea","-Djava.class.path=/path/to/classes")```如果类包含在jar文件中,则将jar文件的路径添加到类路径中,多个路径用分隔符(在Windows上是分号,在类Unix系统上是冒号)分隔。3.然后,我们就可以使用这个类:```pythonMyClass=jpype.JClass("com.example.MyClass")```4.如果类路径设置不正确,会抛出`jpype.JException`(实际上是Java的ClassNotFoundException在Python端的映射)。另外,还要注意:-类路径应该包含所有依赖的jar包和目录。-在启动JVM之后,类路径就不能再改变了(除非重新启动JVM)。所以,确保在启动JVM前设置好。常见错误:类名拼写错误,或者包路径不正确。所以,用户应该检查:1.类路径是否正确设置?2.类名是否拼写正确(包括大小写)?3.是否已经将包含类的jar文件或目录加入到类路径中?如果用户使用的是maven或gradle管理的项目,可能需要将依赖的所有jar文件都加入类路径。可以使用通配符(在类Unix系统上使用`*`)来添加一个目录下的所有jar文件,但要注意JPype的类路径参数中不支持通配符,需要手动列出所有jar文件,或者使用一个循环来构建类路径字符串。在类Unix系统上,可以这样构建包含所有jar文件的类路径:```pythonimportosclasspath=":".join([os.path.join("/path/to/libs",x)forxinos.listdir("/path/to/libs")ifx.endswith(".jar")])```在Windows系统上,将分隔符换成`;`。然后启动JVM:```pythonjpype.startJVM(jpype.getDefaultJVMPath(),"-ea","-Djava.class.path="+classpath)```另外,如果用户使用的是JPype1,那么上述方法是有效的。对于JPype0,用法类似,但启动方法略有不同(例如,使用`jpype.startJVM`的参数传递方式可能不同)。如果用户使用的是JPype1(推荐使用),可以通过pip安装:`pipinstallJPype1`最后,如果用户已经启动了JVM,然后发现类路径有遗漏,那么只能关闭JVM再重新启动(设置正确的类路径)。重新启动JVM需要在当前Python进程内第一次启动(因为一个进程只能启动一次JVM)。如果需要多次调整,可能需要重启Python解释器。因此,总结解决方案:1.确认类路径(classpath)包含所有需要的目录和jar文件。2.在启动JVM时,通过`-Djava.class.path=`参数设置正确的类路径。3.检查类名是否正确。如果问题仍然存在,可以尝试在Java端测试:使用相同的类路径能否从Java命令行运行这个类?例如:java-cp/path/to/classescom.example.MyClass这样可以确认Java类是否可用。下面给出一个完整的示例:假设我们有一个Java项目,其目录结构如下:/projectlibs/dependency1.jardependency2.jarout/com/example/MyClass.class那么,我们可以这样设置类路径(在Linux或Mac上):```pythonimportjpypeimportos#构建类路径:包括out目录和所有jar文件classpath_dirs=["/project/out"]#包含.class文件的目录classpath_jars=[os.path.join("/project/libs",x)forxinos.listdir("/project/libs")ifx.endswith(".jar")]classpath=":".join(classpath_dirs+classpath_jars)jpype.startJVM(jpype.getDefaultJVMPath(),"-ea","-Djava.class.path="+classpath)#然后加载类try:MyClass=jpype.JClass("com.example.MyClass")#使用这个类...exceptjpype.JExceptionasex:print("Classnotfound:"+str(ex))```在Windows上,将构建类路径时的分隔符改为分号,即:classpath=";".join(classpath_dirs+classpath_jars)通过以上步骤,应该能解决ClassNotFoundException。如果用户遇到其他问题,可以提供更多的上下文信息。最后,生成相关问题:§§相关问题§§1.JPype如何设置JVM的启动参数?2.在JPype中如何处理Java异常?3.JPype支持多线程吗?有什么需要注意的?4.如何在Python中调用Java的静态方法?5.如何关闭JPype启动的JVM?</think> ### Python JPype ClassNotFound Exception 解决方案 在使用 JPype 调用 Java 代码时出现 `ClassNotFoundException` 通常由以下原因引起: #### 1. **类路径未正确配置 (最常见原因)** Java 虚拟机 (JVM) 启动时未包含目标类的路径: ```python import jpype # 正确设置 classpath(注意路径分隔符:Windows用; Linux/Mac用:) jvm_path = jpype.getDefaultJVMPath() class_path = [ "your/java/project/target/classes", # 编译后的.class文件目录 "lib/dependency.jar" # 依赖的JAR文件 ] jpype.startJVM( jvm_path, "-Djava.class.path={}".format(";".join(class_path)), # Windows # "-Djava.class.path={}".format(":".join(class_path)), # Linux/Mac ) ``` **关键检查项**: - 确认路径是否存在文件`os.path.exists("your/java/project/target/classes")` - 检查包名是否匹配:类`com.example.MyClass`需位于目录`.../com/example/MyClass.class` - 使用绝对路径避免歧义:`os.path.abspath("your/path")` #### 2. **包名未正确声明** 当调用带包名的 Java 类时: ```python # 错误:未指定完整包名 MyClass = jpype.JClass("MyClass") # 正确:使用全限定类名 MyClass = jpype.JClass("com.example.MyClass") ``` #### 3. JAR 文件加载异常 ```python # 检查JAR是否可访问 try: from java.util import ArrayList # 测试标准库 print("JVM启动成功") except jpype.JException as e: print(f"JAR加载失败: {e}") ``` #### 4. **类加载器冲突** 当使用第三方库时: ```python # 指定线程上下文类加载器 Thread = jpype.JClass("java.lang.Thread") thread_class_loader = Thread.currentThread().getContextClassLoader() custom_class = thread_class_loader.loadClass("com.example.CustomClass") ``` #### 5. 其他可能原因 - JVM 未启动:确认`startJVM()` 已被调用 - 类名拼写错误:区分大小写 - JDK/JRE 版本不兼容 #### 📌 完整排查流程 1. 用`java -cp your_path your.ClassName` 在命令行测试类是否可访问 2. 检查 JPype 日志 `jpype.enableLogging()` 3. 简化测试: ```python jpype.startJVM() System = jpype.JClass("java.lang.System") System.out.println("Hello from Java!") #
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值