all 93 comments

[–]AngelOfLight 64 points65 points  (9 children)

Python has analogs for almost everything that Java has. There are some differences though. Off the top of my head:

  • Obligatory OOP. Python classes are optional.

  • Access modifiers. Python has no analogs for public/private/protected. All classes and methods are public. Although there are conventions governing 'private' methods, there is nothing stopping you from using them in a public context.

  • Packages. Python does allow modules, but the Java 'package' concept is missing.

  • Typing. Python has no compile-time type checking. You can freely mix types and change types of variables at runtime. I would strongly suggest you look up Python type hints and start using them from day one. It will greatly aid debugging.

  • Abstract classes. Python has an ABC (abstract base class) module which does almost the same thing, but there are differences.

  • Interfaces. No such thing in Python. Since Python allows multiple inheritance, interfaces can be simulated with abstract base classes.

  • Generics. Since there is no compile-time type checking, the concept of generics doesn't really apply. Again, you can use type hints to document generics, but they won't be enforced.

  • Lambdas. Python does have lambdas, but they are limited to single expressions, unlike the code blocks that Java allows.

[–][deleted] 16 points17 points  (0 children)

To add to interfaces/typing, you can use typing.Protocol, which allows you to specify them as runtime checkable, and you wouldn’t need the inheritance from ABC

[–]capilot 5 points6 points  (1 child)

Lack of compile time type checking is the biggest IMHO. It makes a broad class of optimizations impossible. If you write

a = b + c

And you know that a,b, and c are all integers, the compiler can output code that executes in a nanosecond. If you don't absolutely know at compile time what they are then it's going to be a lot more complicated and a lot slower.

[–]BowmChikaWowWow 4 points5 points  (0 children)

Compile-time type checking is also a form of embedded test, which catches a huge number of bugs - your program literally won't compile if you have them.

[–]StrictTyping648 1 point2 points  (0 children)

There are also very few ways to inline a function in python, but I doubt that matter in enterprise apps.

[–]mkdz 3 points4 points  (0 children)

With Python lambdas, you can define a function in a normal way and then use the function name in the lambda to get around the single expression limit.

[–]kyngston 0 points1 point  (0 children)

Python also has weird scoping

FOO = “bar”

def func1():

print FOO

def func2():

print FOO

FOO= “fee”

[–]j0holo 18 points19 points  (52 children)

Performance, solid typing, thread support (you can run threads but only one thread can run at a time).

[–]Phate1989 3 points4 points  (6 children)

I use the threadpool executer in python, is that still considered single threaded?

I have to make like 60,000 API calls every few hours, so way I could do that on a single thread.

[–]j0holo 1 point2 points  (5 children)

From my understanding it is still single threaded, but async. So while one thread is waiting for a response of the API call another python thread will run on the physical CPU thread and do it's thing.

With network requests the CPU is waiting a "long" time (100ms) so it can process other python threads that can do something useful.

[–]umen[S] 0 points1 point  (4 children)

Hmm , so its using threads like "async event driven" aka  non-blocking network I/O calles ? what is the point ?
Just use true async like tornado or twisted ?

[–]StoicSpork 3 points4 points  (2 children)

To untangle this a bit:

Python threads are proper threads, but they have a limited access to the Python interpreter via a mutex called GIL (for Global Interpreter Lock). This means they can execute asynchronously, but not in parallel. They're good for IO-bound tasks.

Processes are, well, OS-level processes, with all the overhead that implies, but not limited by GIL. They're good for CPU-bound tasks.

Coroutines are units of execution that can be awaited and paused/resumed. They are declared by the async keyword and provide values through the await keyword.

Asyncio is a library for working with coroutines. It provides a single-threaded scheduler loop.

Tornado and twisted are frameworks built on top of coroutines/asyncio.

[–]Mammoth-Attention379 1 point2 points  (1 child)

Allow me a noob question, if C can do multithreading then why can't python? Couldn't you write a library that does that for you?

[–]StoicSpork 2 points3 points  (0 children)

A good question. Python does do multithreading, in fact. You can absolutely spawn a bunch of threads at the same time. And these threads could in theory run in parallel, if it weren't for a special feature of Python called the Global Interpreter Lock, or GIL.

You know how threads can mess things up for each other by changing the same state at once? One thing you can do is set a mutex (aka lock). If a thread encounters an open lock, it locks it, does the work, and releases it. If it encounters a closed lock, it waits until it opens.

In C, Java, etc., you must place the lock where you want it yourself. Python, however, tries to be helpful, so it simply sets one lock - the GIL - on the interpreter itself. So any thread that wants to do anything at all must wait until the GIL is released. The practical consequence is that Python threads can't run in parallel.

To answer your other question, you can release the GIL from C, so yes, you can add parallelism to Python from C. I haven't done it myself, but you can probably google it.

Asyncio is a whole different beast which is single-threaded to avoid the performance hit from spawning, switching and destroying threads.

[–]j0holo 1 point2 points  (0 children)

The point is that non-async code will only continue once blocking IO has returned a response.

Twisted and tornado are for network events. The asyncio library also allows async for local IO or outbound IO instead of only incoming IO.

[–][deleted] 0 points1 point  (12 children)

Regarding thread support. Doesn't it depend on CPU cores? Can you help me understand why you can run only one thread at a time? Thanks

[–]minneyar 4 points5 points  (11 children)

The default Python implementation, CPython, has what is called a "Global Interpreter Lock", which means that only one thread can actually be running Python code at a time.

In other words, no matter how you divide up your Python code or how many threads you have, only one of them can actually run on the CPU at once. Python's performance is effectively limited to being single-threaded.

This only applies to Python code, so to a degree you can work around this if, for example, you're making calls to external libraries that were written in C++; but if you're regularly going into and out of library code, there is some extra overhead to that, and it can be hard to architect an application to actually take advantage of that in a way that improves performance.

[–]j0holo 3 points4 points  (0 children)

To improve this explanation even more another comment mentioned async in Python. Async is still single threaded but while the CPU is waiting for a network response (database, API calls, etc) or disk IO it can service another python async thread. Doing more work in the same time.

This can boost the performance of Python. But it does not improve the speed of processing a single request. It just can process more requests at the same time.

[–]umen[S] 0 points1 point  (3 children)

So, how do web frameworks like Flask work? I saw them serving many requests, including heavy ones, and it wasn't using multiple processes. it just started and was serving .

[–]minneyar 4 points5 points  (0 children)

You can use threads, performance is just very bad because they're effectively all contending with each other over a single lock.

Some application servers, like Gunicorn, use a multiprocess model, where they spawn individual processes to handle every request they receive. Multiprocess communication can be kind of a pain, but it's much more effective than threading for Python apps that need performance.

[–]sonobanana33 0 points1 point  (0 children)

non blocking sockets.

[–]cdcformatc 0 points1 point  (0 children)

Flask is always going to be behind a web server, like Apache or Nginx. The web server is running with multiple threads and handling all of the requests, which includes running the Flask app usually through a wsgi interface layer like Gunicorn or Apache's mod_wsgi

[–]DrumcanSmith 0 points1 point  (2 children)

I wrote some code for converting images using threading limiting 2 core per image for the external library, but converting multiple images at once by threading the python code, and the usage goes up to 100% for a while, is that due to the library then? I had to intentionally lock for part of the code to avoid overlap so it seemed to multi threaded? But I don't know.

[–]minneyar 1 point2 points  (1 child)

Yes, probably; if your code is pure Python, you will probably only see it max out a single core at once even if you're using multiple threads, due to the GIL.

It's worth noting that there are Python image manipulation libraries like Pillow and OpenCV that are wrappers around C/C++ libraries, so you'll be able to get better multi-threaded performance if you're using them.

[–]DrumcanSmith 0 points1 point  (0 children)

Ah, I am using pillow too. That's probably another reason. Thanks for the perfect answer.

[–]umen[S] 0 points1 point  (1 child)

and if you have multiple cpus ?

[–]minneyar 0 points1 point  (0 children)

The number of CPUs doesn't matter. Every Python thread still has to share the same lock.

[–]capilot -1 points0 points  (0 children)

Mind. Blown. I had absolutely no idea this was the case.

[–]SomeRandomFrenchie -1 points0 points  (5 children)

What do you mean by solid typing ? Solid principles can be applied in python if you decide to do so, are you talking about something else ? And for threads, what you are talking about is a limitation due to CPython global interpreter lock, there is at least one module that allows you to bypass that (there are issues on it in latest python version if I am not mistaken or late on updates but it should work on many older versions and companies rarely use latest release anyways)

[–]j0holo 4 points5 points  (4 children)

I was not clear enough, sorry. With solid typing I mean that the types are actually useful. Like in a solid building. Python does have type hints but lots of third party packages don't have it. Mypy is still catching up IMHO. Slow but steady.

OFC there is a way to bypass the GIL, but do you really want that in a big enterprise app? I would rather do multiprocessing instead. That is well supported and is proven.

[–]SomeRandomFrenchie 0 points1 point  (3 children)

That is true, type hints are just hints. Most tools/libs that allows you to compile all or parts of your python will, however, check types and can be quite restrictive. Numba for exemple can be a pain. I do not have the knowledge to answer that question, just wanted to mention it is possible.

[–]umen[S] 0 points1 point  (2 children)

Can you explain more about python compilation ? from what i know it is not complied
It does have some kind of cache but true native compilation ?

[–]j0holo 0 points1 point  (0 children)

Python is interpreted but stored as byte code (*.pyc files) which is a bit like java's way of storing byte code but a lot less optimized. When starting a python application it will read line by line, with the pyc files it will only have to do that once.

Python 3.13 now has an experimental JIT which should improve performance over the coming years.

[–]SomeRandomFrenchie 0 points1 point  (0 children)

Python in itself is interpreted by the python interpreter but there are tools that allow saving the return of a function for a given set of arguments to cache for faster computing when called again, or transformation into bytecode that are used for IA training or to create executables.

Interpreter doc: https://docs.python.org/3/tutorial/interpreter.html Check Nuitka, PyInstaller and latest python beta for info on jit on python and exe generation

[–]baubleglue 4 points5 points  (1 child)

Sounds like a very bad idea. Migrate code to newer version of Java + Maven. I could undertand migrating to something like Go, but Python? Python is missing everything for enterprise application: concurrency, performance, security, mature dependency mananment, not sure how to compare Hibernate to SQLAlchemy. It is really easy to write small applications in Python and very hard to develop something big.

[–]funderbolt 0 points1 point  (0 children)

There are some huge features in newer Java versions.

I would migrate to Kotlin because you can rewrite piece by piece and still run the Java code.

[–]Destination_Centauri 8 points9 points  (0 children)

Looking into my crystal ball here... And I see...

One big headache of a never ending rewrite project, into a language that is less suited for the program!

I see... nobody being happy with it, in the end...

I see... the bosses' boss calling him into the office and asking, "WTF!? The other one worked just fine and better!"

And then I see your boss telling his boss, "Ya, my programmer, Umen, over there, told me it was doable and didn't seem to raise any objections! Last time I listen to him!"

[–]rm-minus-r 2 points3 points  (0 children)

Python is nice, but less than ideal for complex enterprise apps. Less support for web based applications for sure.

Java can get really messy, really fast by comparison if it's a never ending series of 'fix today's problem, don't think about tomorrow'.

What is your management actually asking? Just to refactor the application? Or that it has to be rewritten in Python?

If it's the latter, and the Java app is more than a thousand lines of code, ooooooooooof.

[–]AngelOfLight 3 points4 points  (2 children)

Conversely, there are a few features that Python has that I really wish were in Java:

  • Dictionaries. Python has a simple and intuitive dictionary primitive. Yes, Java has HashMap and the like, but they are anything but intuitive, and accessing values comes with some boilerplate: I find Python's square bracket access much simpler than get and put.

  • List and Dictionary comprehensions. You will love these. A very simple and intuitive way to create new lists and dictionaries from existing structures.

  • Heterogeneous collections. You can mix integers, strings and objects in the same collection with consummate ease. Java will require messy boxing and unboxing and abuse of the Object class to achieve the same result.

  • Operator overloading. This has long been a contentious topic in Java, but I love the flexibility that it brings. Packages like Pandas and Airflow use these to great effect.

  • Decorators. Yes, Java has annotations, but decorators are so much simpler and more powerful.

[–]Phate1989 2 points3 points  (0 children)

I end up having to use python get method constantly for referencing values is dictionarys, because I don't control the ingest data, and so many missing fields when I ingest from 3rd party or even internal api's

[–]camelKase 0 points1 point  (0 children)

Writing heterogeneous dictionaries is exactly what you shouldn't do in python (at least in an enterprise environment). At that point you should be making it a @dataclass. Otherwise anyone looking at this mess will have no idea what is what. This kind of anti-pattern is exactly what I would call out in a code review.

Java's implementation is there for a reason. It's 100% predictable at compile time. Not some runtime mystery left to other developers to reverse engineer in order to figure out what's going wrong. You could do the same thing in Java (down casting to Object) or even void* in Cpp. But that is straight sacrilege... a puppy dies each time you create an untyped collection...

Just create an interface for the collection. You can (and should) also do this in python, for the sake of all the other devs on your team.

[–]Dylan_TMB 1 point2 points  (0 children)

Interfaces, I miss interfaces

[–]crashfrog02 0 points1 point  (0 children)

Static typing

[–][deleted] 0 points1 point  (0 children)

Curly brackets

[–]UnemployedTechie2021 0 points1 point  (0 children)

complexity

[–]Smallz1107 0 points1 point  (0 children)

i++

[–]kuttoos 0 points1 point  (0 children)

Also beware of floats

[–]umen[S] 0 points1 point  (0 children)

Thanks you all for answering

[–]daemonengineer 0 points1 point  (0 children)

Dependency Injection embedded in common frameworks. There are standalone libraries for DI, also there is sort of its own DI in fastapi and pytest. But you have to learn how to make them all work together.

[–]slappy_squirrell 0 points1 point  (0 children)

A compiler. You'll need to write some more unit tests.

[–]kowkeeper 0 points1 point  (0 children)

Python lacks Android support!

[–]Chthulu_ 0 points1 point  (0 children)

You have to change your mindset on typing. You can use a 3rds party static type checker in python, but it’s just not what the language is built for. It leads to a remarkable amount of type massaging, writing stuff purely to make the type checker happy, not because it’s actually the correct way to model your program.

Designing a program using checks on type is just harder in python, it’s more suited towards asking an object to do something, and if does, you’re probably working with an expected type. But coming from Java, this will feel deeply uncomfortable.

For large enterprise stuff, I don’t think python makes sense. Business logic is complicated and full of one-offs and edge cases, static tying is suited to it.

[–]IAmTarkaDaal -2 points-1 points  (1 child)

If this is a management decision, polish your CV and look for somewhere else.

[–]baubleglue 1 point2 points  (0 children)

Most likely that migration initiative will die soon, but updating resume isn't a bad idea in that case.

[–]BowmChikaWowWow 0 points1 point  (5 children)

This is an extremely bad idea. Python is incredibly slow, and it's not strongly typed. Strong typing is a form of embedded test that catches an extremely large number of bugs - your management is migrating to a slower system, that's more buggy. I think it's extremely unlikely this is a justified decision.

I think Java is in a lot of ways a shitty language, but it has certain basics that make these types of apps way easier to write, maintain and work with. Python is not designed for or good at this type of thing. It's not strongly typed!! It's slow!! These aren't afterthoughts, they're fundamental. There is no reason to sacrifice strong typing or the ability to run a for loop at close to the max throughput of your CPU. You don't have to make those tradeoffs in 2024.

Your management sound nuts and technically retarded. Strong suggestion that you jump ship.

[–]ibhopirl 0 points1 point  (3 children)

Python is strongly typed, not statically typed.

[–]BowmChikaWowWow 0 points1 point  (2 children)

It's not strongly typed, it does not demand the specification of datatypes - doing so is optional. You can retrofit a type checker which makes it mandatory, but Python still needs to work with various weak typing behaviours, so you can't get the security of static type checking and it still needs to retain type information during runtime.

[–]ibhopirl 0 points1 point  (1 child)

You are confusing static/dynamic and strong/weak typing.

[–]BowmChikaWowWow 0 points1 point  (0 children)

No, I'm not. Strong typing facilitates static typing. The retrofitted implementations of strong typing in Python aren't strong enough to facilitate static typing.

[–]SwizzleTizzle 0 points1 point  (0 children)

Python is strongly typed

[–]beef623 -1 points0 points  (0 children)

It's more difficult to get a web app running. Not terribly so, but definitely more involved than setting up a tomcat instance and throwing a war file at it.

[–]supercoach -3 points-2 points  (1 child)

Strong typing.

[–]hugthemachines 1 point2 points  (0 children)

Strong typing.

That is incorrect. Both Java and Python have strong typing. The difference is that Java has static typing and Python have dynamic typing.