The Python Rabbithole

The Python Rabbithole

Share this post

The Python Rabbithole
The Python Rabbithole
16 Python Function Things I Regret Not Knowing Earlier

16 Python Function Things I Regret Not Knowing Earlier

Liu's avatar
Liu
Aug 03, 2024
∙ Paid
3

Share this post

The Python Rabbithole
The Python Rabbithole
16 Python Function Things I Regret Not Knowing Earlier
Share

1) Type hinting in functions

I started off learning Python without using type hints at all. And so for the first few years of writing Python, I never wrote a single type hint.

As a simple example, let’s say we want to write a function that finds the average of 2 integer numbers. How I used to write my function:

How I write my functions now at work (adding type hints and docstring):

  • a: int means that a should ideally be an integer

  • b: int means that b should ideally be an integer

  • -> float means that the function should ideally return a float value

However, do note that type hints hint, but do not enforce. If we pass in strings or other data types into a and b, Python is actually ok with that (until it attempts to divide a string by 2 which causes error)

The purpose of type hinting is more of 1) making code readable for humans and 2) enabling your IDE eg. PyCharm or VSCode to do checks for you.

As much as possible, try to add type hints into your functions so that other programmers (or even future you) will have an easier time understanding the function.

2) Default arguments

^ in the above function, greeting=‘hi’ is a default argument.

  • if we decide to not pass anything to greeting, it is automatically assigned ‘hi’

  • if we decide to pass something to greeting, it takes that value

In greet(‘tom’) , we don’t pass anything into greeting. Which means it defaults to ‘hi’

In greet(‘tom’, greeting=‘hello’) , we pass ‘hello’ into greeting. Which means that we override the default argument, and assign greeting = ‘hello’

This is useful if we have a large number of parameters in a function, and do not wish to have to pass in every single parameter each time we call the function.

3) Arguments VS Parameters

In my first few years of learning Python, I thought they meant the same thing.

But not really. There’s a slight difference.

Let’s say we have a simple function that takes in (a, b) and returns their average.

A parameter is a variable written inside the brackets when we define our function. Here, a and b are parameters.

An argument is the value that we actually pass into our function when calling it. Here, when we call the avg(a, b) function, 3 and 5 are arguments.

4) Positional VS Keyword Arguments

A simple trivial function just to show an example.

Let’s call this function by passing in some positional arguments.

^ here, 4 and 7 are positional arguments. Positional arguments need to be in order — 4 is passed into a, while 7 is passed into b.

Next, let’s call our function by passing in some keyword arguments.

^ here, b=5 and a=8 are keyword arguments. Keyword arguments do not need to be in order, but we must pass them in using a key=value format.

5) Arbitrary Positional Arguments (*args)

Arbitrary positional arguments in a function, also known as *args, allow the function to accept any number of positional arguments.

^ here, the test function takes in *args — which allows test to take in any number of positional arguments, which will all be caught in a tuple named args.

We can combine this with normal parameters too (the *args must be behind)

Additional note — we don’t have the name it *args. We can name it whatever as long as we add the * in front of it.

6) Arbitrary keyword arguments (**kwargs)

Arbitrary keyword arguments, also known as **kwargs, allow our functions to take in any number of keyword arguments.

^ here, the test function takes in **kwargs — which allows test to take in any number of keyword arguments, will be caught in a dictionary named kwargs

We can also combine this with normal parameters (**kwargs must be behind too)

Additional note — we don’t have to name it kwargs if we don’t want to. We can name it whatever we want as long as it has ** in front of it.

Quick Pause

I recently wrote a book — 101 Things I Never Knew About Python

Check it out here if you wish to support me as a writer!

Link: https://payhip.com/b/vywcf

7) Using * and ** to pass list/dict into function

Here’s a trivial function that simply prints out its arguments.

Instead of calling this function the normal way ie hi(1, 2) , we could:

1) use * to pass a list containing positional arguments

^ here, the * in front of nums unpacks its contents into the function call as positional arguments. This is the same as hi(100, 200)

2) use ** to pass a dict containing keyword arguments

^ here, the ** in front of d unpacks its key-value pairs into the function call as keyword arguments. This is the same as hi(a=100, b=100)

8) Function vs method

When I was still starting out in Python, I used these 2 terms interchangeably. But there are slight differences in the terms.

A method is simply a function that belongs inside a class.

9) Magic methods in classes

In classes, magic methods are special methods that define special behaviour.

Magic methods usually start and end with double underscores eg. __init__, __str__ etc. Hence, magic methods are also known as dunder methods.

Each magic method has a its own unique purpose and special behaviour. For instance, the __init__ method defines how attributes of an object are assigned to it.

The __str__ method defines what string is returned when we do str(obj) to our custom object obj:

There are many more magic methods that each have their unique use cases and behaviour, but I won’t go through all of them here.

Some useful ones I use — __add__, __eq__, __lt__, __gt__, __invert__ etc

10) Higher-order functions

A higher-order function is a function that either:

  1. takes in another function as an argument

  2. returns another function

  3. both 1 and 2

Example of 1) function that takes in another function as an argument

^ here, the higher-order function apply takes in another function square as an argument, and applies square to every single element in the list ls.

Example of 2) function that returns another function

^ here, the higher-order function add_n returns another function. add_n(100) returns a function that takes in x, and adds 100 to x.

11) Lambda functions

We usually define functions using the def keyword. But did you know that we can also define functions using the lambda keyword?

Here’s a trivial, simple function that simply adds 2 numbers together.

Here’s how we can define the same exact function using the lambda keyword.

  • function parameters go before the colon

  • the returned value goes after the colon

Lambda functions are also called anonymous functions. This means that it is optional to give a lambda function a name.

In the above example, we choose to give it a name add. But we can decide not to give it a name if we really don’t want to.

Lambda functions are useful when pairing them with higher-order functions.

^ here, we pair a lambda function with the higher-order function apply so that we don’t have to define another function using the def keyword.

Keep reading with a 7-day free trial

Subscribe to The Python Rabbithole to keep reading this post and get 7 days of free access to the full post archives.

Already a paid subscriber? Sign in
© 2025 ZL Liu
Privacy ∙ Terms ∙ Collection notice
Start writingGet the app
Substack is the home for great culture

Share