Function Definition Basics

by Christoph Schiessl on Python

Today, I want to kick off a series of articles to explore Python's Function Definition statement. Superficially, this is a really simple topic, but as always, the devil is in the details. I believe almost all developers will discover new features they have never even heard of before, or at least they will discover some edge cases they are unaware of.

To prove my point, here is the full grammar for Function Definitions in Python, taken from the official Language Reference:

funcdef                   ::=  [decorators] "def" funcname [type_params] "(" [parameter_list] ")"
                               ["->" expression] ":" suite
decorators                ::=  decorator+
decorator                 ::=  "@" assignment_expression NEWLINE
parameter_list            ::=  defparameter ("," defparameter)* "," "/" ["," [parameter_list_no_posonly]]
                                 | parameter_list_no_posonly
parameter_list_no_posonly ::=  defparameter ("," defparameter)* ["," [parameter_list_starargs]]
                               | parameter_list_starargs
parameter_list_starargs   ::=  "*" [parameter] ("," defparameter)* ["," ["**" parameter [","]]]
                               | "**" parameter [","]
parameter                 ::=  identifier [":" expression]
defparameter              ::=  parameter ["=" expression]
funcname                  ::=  identifier

Doesn't look trivial at all, right? The grammar will be our guide because it must, by definition, describe all the features that Python has to offer. The simplest function that we can possibly define is something like the following:

def foo():
    pass

As you can see, this is a function named foo, which doesn't take any parameters and doesn't return anything. So, let's put it into a console session so that we can play with it:

Python 3.12.1 (main, Jan  1 2024, 16:22:47) [GCC 13.2.1 20230801] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> def foo():
...     pass
...

The function is defined now, and we can call it without parameters:

>>> foo()
>>> print(foo())
None
>>> type(foo())
<class 'NoneType'>

As you can see, it returns nothing, or rather it returns None, which is the only instance of NoneType. However, if we do supply positional parameters or keyword parameters, it raises a TypeError exception, informing us about the unexpected parameters:

>>> foo(1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: foo() takes 0 positional arguments but 1 was given
>>> foo(a=1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: foo() got an unexpected keyword argument 'a'

So far, so good. Next up, we can ask Python about the type of foo:

>>> type(foo)
<class 'function'>

And this is very interesting because functions are not special; they are just instances of the class function. So, knowing that functions are, in fact, function objects, we can ask Python to give us the list of methods and properties that are defined on our function object:

>>> dir(foo)
['__annotations__', '__builtins__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__getstate__', '__globals__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__type_params__']
>>> len(dir(foo))
38

Our trivial function has 38 methods and properties, and all of them are dunder methods! Just to give you one example, we can ask the function object foo to give us the name with which it was originally defined:

>>> foo.__name__
'foo'
>>> bar = foo
>>> bar.__name__
'foo'

Long story short, functions are objects, and these objects come with a rich set of features that we can leverage in numerous ways. I hope this short introduction has piqued your curiosity and made you eager to learn more. As always, please don't hesitate to reach out if you have any comments or questions.

Christoph Schiessl

Hi, I'm Christoph Schiessl.

I help you build robust and fast Web Applications.


I'm available for hire as a freelance web developer, so you can take advantage of my more than a decade of experience working on many projects across several industries. Most of my clients are building web-based SaaS applications in a B2B context and depend on my expertise in various capacities.

More often than not, my involvement includes hands-on development work using technologies like Python, JavaScript, and PostgreSQL. Furthermore, if you already have an established team, I can support you as a technical product manager with a passion for simplifying complex processes. Lastly, I'm an avid writer and educator who takes pride in breaking technical concepts down into the simplest possible terms.

Continue Reading?

Here are a few more Articles for you ...


Function Definition with Simple Parameters

Learn about functions with simple parameters in Python, including how the called can decide to use positional or keyword notation.

By Christoph Schiessl on Python

Function Definition with Keyword-Only Parameters

Learn about keyword-only parameters in Python, how to define them, and their interplay with position-only parameters.

By Christoph Schiessl on Python

Function Definition with Default Parameters

Learn about Python functions with default parameters. Understand how default parameters work and some essential restrictions and evaluation rules.

By Christoph Schiessl on Python

Web App Reverse Checklist

Ready to Build Your Next Web App?

Get my Web App Reverse Checklist first ...


Software Engineering is often driven by fashion, but swimming with the current is rarely the best choice. In addition to knowing what to do, it's equally important to know what not to do. And this is precisely what my free Web App Reverse Checklist will help you with.

Subscribe below to get your free copy of my Reverse Checklist delivered to your inbox. Afterward, you can expect one weekly email on building resilient Web Applications using Python, JavaScript, and PostgreSQL.

By the way, it goes without saying that I'm not sharing your email address with anyone, and you're free to unsubscribe at any time. No spam. No commitments. No questions asked.