Learning Python When You Already Know Java

Java to Python is one of the more common transitions in the industry, and it's mostly easy. The places it's not easy are the places where Python's flexibility produces patterns that look wrong to a Java brain.

Tech Talk News Editorial7 min read
ShareXLinkedInRedditEmail
Learning Python When You Already Know Java

Going from Java to Python is one of the more common transitions in software, and it's mostly straightforward. The control flow is the same. The class model is similar. Most of what a Java developer knows transfers directly. The places where Python diverges (mutability defaults, comprehensions, the data model) are the ones where Java intuitions actively work against you. Knowing where to expect the friction is most of the value.

The way I think about the transition is that Python rewards small functions and standard library knowledge in ways Java doesn't. A Java developer who tries to write Python like Java ends up with code that's technically correct and obviously not Pythonic. The good news: the gap closes fast. Most strong Java developers write reasonable Python within a few weeks of consistent work.

Context

This article assumes you can already write working Python code at the syntax level (loops, conditionals, function definitions). The focus is on the conceptual model differences and the places where Java habits cause problems.

What Transfers Directly

Most of Java translates one-to-one:

  • Conditional logic (if/else, switch becomes match in Python 3.10+).
  • For and while loops.
  • Try/catch (called try/except in Python).
  • Classes, methods, inheritance.
  • Static and instance methods.
  • Method overriding.

If your code consists mostly of these constructs, Java to Python is largely a syntactic rewrite. The Python version is shorter and uses snake_case instead of camelCase.

The First Real Surprise: Mutable Default Arguments

Java defaults arguments to fresh values on each call. Python evaluates default arguments once, when the function is defined. This produces a famous bug:

The famous Python footgunPython
def add_to_list(value, target=[]):
    target.append(value)
    return target

print(add_to_list(1))   # [1]
print(add_to_list(2))   # [1, 2]  ← surprise
print(add_to_list(3))   # [1, 2, 3]

The default empty list is shared across all calls. Java developers expect a fresh list each call. The idiom in Python is:

The fixPython
def add_to_list(value, target=None):
    if target is None:
        target = []
    target.append(value)
    return target

Every linter will catch this, but you'll write it once or twice before the lesson sticks.

List Comprehensions Replace Most for Loops

In Java, you'd build a list with a for loop:

Javajava
List<Integer> doubled = new ArrayList<>();
for (int n : nums) {
    if (n > 0) {
        doubled.add(n * 2);
    }
}

In Python, this is a one-liner:

PythonPython
doubled = [n * 2 for n in nums if n > 0]

Comprehensions exist for lists, dicts, sets, and generators. They're the single most idiomatic feature in Python. A Java developer who keeps writing four-line for loops to build lists will produce code that experienced Python reviewers will rewrite. Learning to spot the comprehension opportunity is one of the highest-impact moves in the transition.

Duck Typing Replaces Interfaces

Java has explicit interfaces. A type has to declare it implements an interface to be used where the interface is expected. Python uses duck typing: if it walks like a duck and quacks like a duck, it's a duck.

PythonPython
def print_each(items):
    for item in items:
        print(item)

# Works with anything iterable: list, tuple, set, dict keys,
# generator, custom class with __iter__
print_each([1, 2, 3])
print_each({"a", "b"})
print_each(my_custom_iterable)

The Python equivalent of a Java interface check is “just call the method and see if it works.” This is more flexible and harder to reason about for someone trained on Java's explicit type contracts. The Python community has added Protocols (PEP 544) for cases where you want explicit duck-typing contracts, but most code doesn't use them.

Everything Is an Object, Including Functions

Java added first-class functions in version 8 with lambdas. Python has had them from day one. Functions can be passed around, returned from other functions, stored in lists, and assigned to variables.

First-class functionsPython
def add(a, b):
    return a + b

operations = [add, max, min]
result = operations[0](2, 3)  # 5

def apply(fn, x, y):
    return fn(x, y)

apply(add, 2, 3)  # 5

Higher-order functions are everyday Python. Java's functional interfaces and lambdas are doing the same thing under the hood, but the syntax is heavier and the convention is to declare types. In Python you just pass the function.

Standard Library Knowledge Matters More

Java's standard library is wide but tends to require explicit usage. Python's standard library has tools that make code dramatically shorter if you know they exist. Some examples:

  • collections.Counter: One-line frequency counts.
  • itertools: chain, permutations, combinations, groupby.
  • functools: reduce, partial, lru_cache.
  • pathlib.Path: File path manipulation that replaces most os.path code.
  • contextlib: Context managers without writing classes.

Java developers tend to reimplement these by hand. The fastest path to writing idiomatic Python is reading the standard library docs and getting a feel for what's already built.

Mistakes Java Developers Make in Python

The patterns I've seen repeatedly:

  • Writing classes for everything. Many Java patterns (Singleton, Builder, Factory) don't need classes in Python. A module with module-level functions is often the right answer.
  • Using getter and setter methods. Python uses public attributes by default. The @property decorator gives you computed attributes when you actually need encapsulation.
  • Catching broad exceptions. Java's checked exceptions teach you to wrap things in try/catch (Exception e). In Python, this swallows bugs. Always catch specific exception types.
  • Avoiding mutability everywhere. Python's standard idioms (mutating a dict in place, sorting a list in place) are normal. Java developers sometimes go to absurd lengths to write “functional” Python that's harder to read than the imperative version.
  • Premature optimization with type hints. Type hints are useful at module boundaries. Annotating every variable inside a function adds noise without much benefit.

Tooling You Should Set Up Day One

The Python ecosystem has converged on a few standard tools that catch most beginner mistakes:

  • uv or pip + venv for dependency management.
  • ruff for linting (replaces flake8, isort, and most of pylint).
  • black for code formatting (or ruff format).
  • mypy or pyright for type checking when you use type hints.
  • pytest for testing.

Setting these up before you write your first real project saves you from a class of mistakes that the Java toolchain catches automatically.

Takeaway

Most Java knowledge transfers cleanly. The friction is mutable defaults, comprehensions replacing for loops, duck typing replacing interfaces, and the cultural preference for module-level functions over classes. Java developers who absorb these conventions write good Python within weeks. The ones who don't write “Java with snake_case” for years.

The Take

The fastest way to learn Python coming from Java is to read other people's Python. Pick a small open-source project (Flask, Click, requests) and read 500 lines of source code. You'll learn the idioms, see how comprehensions are actually used, and notice when classes appear and don't. Documentation alone teaches you the syntax. Reading code teaches you the culture, which is most of what makes Python feel different.

Written by

Tech Talk News Editorial

Tech Talk News covers engineering, AI, and tech investing for people who build and invest in technology.

ShareXLinkedInRedditEmail