I have recently started to learn python, and coming from Java there are a few interesting things I have found about functions.
The first thing that stuck out to me is that in Python, functions are values. What I mean by that, is a function is a thing that you can store, just like you can store a number or a string. For example, let's look at this simple function below which squares a number:
def square(item: int) -> int:
return item * item
This simple function can be assigned to a new variable (an alias):
square_2 = square
Therefore, if we wanted to square a number we could either use square_2 or square as they both do the same thing.
You can even pass a function into another function like this:
print(apply_to_each(square, nums))
So when a function doesn't have parentheses after it, it can be passed around and stored as a value. But, if we have a parentheses after, it runs the function instead. Therefore, if we wrote square_2 = square(), that would run the function square() and store the result, not the function itself.
In Java, if I wanted to pass some behaviour into another method like this, I'd usually have to wrap it up in an object first (a class that mimics function behaviour). In python, I can just pass the function straight in, which feels a lot simpler.
Here is the full example of passing a function into another function:
from typing import Callable
nums = [1, 2, 3, 4, 5, 6, 7]
def apply_to_each(fn: Callable, items: list[int]) -> list[int]:
new = []
for i in items:
new.append(fn(i))
return new
def square(item: int) -> int:
return item * item
print(apply_to_each(square, nums)) # [1, 4, 9, 16, 25, 36, 49]
print(apply_to_each(lambda x: x-5, nums)) # [-4, -3, -2, -1, 0, 1, 2]
print(apply_to_each(str, nums)) # ['1', '2', '3', '4', '5', '6', '7']
This is really useful because it allows us to write one general function (apply_to_each) that changes what it does based on the function we pass in. So we don't need to write a new loop every time we might want to do something different to a list.
Another thing I learnt about functions is that when we have a particularly small function, we don't even need to name it. We can use something called a lambda. Here is an example:
lambda x : x * 2
Let's break it down. Lambda here just means I am creating a simple function. Everything before the colon (the x) is the input, and everything after the colon (the x * 2) is what gets given back. So if x = 2, then lambda x : x * 2 gives us 4.
Coming from Java, treating functions as values felt strange at first, but it makes a lot of code shorter and simpler.
Top comments (1)
Nice framing for the jump from Java, and the
squarevssquare()distinction is exactly where people lose a whole afternoon. Passingstrstraight intoapply_to_eachis a good illustration too: that's the case with no clean Java equivalent, since a method reference there always has to carry a receiver.Two things that bit me when I made the same move. Never define a lambda inside a loop and expect the loop variable to be captured per iteration — it's late-bound, so they all see the final value. And prefer
Callable[[int], int]over a bareCallablein annotations, otherwise the type checker accepts anything and you lose the guard rail you had in Java.