GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • ZetCode
  • All
  • Golang
  • Python
  • C#
  • Java
  • JavaScript
  • Subscribe
  • Twitter
  • Github
  • Privacy
  • About

This view

  • Original page
  • New address

GeezerVison is showing zetcode.com — Python function - defining and calling functions, arguments, scope

Ebooks

  • PyQt5 ebook
  • Tkinter ebook
  • SQLite Python
  • wxPython ebook
  • Windows API ebook
  • Java Swing ebook
  • Java games ebook
  • MySQL Java ebook

Python function

last modified October 30, 2026 Python 3.15

In this article we cover functions in Python.

Python function definition

A function is a named block of code that maps zero or more input parameters to a result. A function is executed only when it is called.

The advantages of using functions are:

  • Code organization
  • Reducing duplication of code
  • Decomposing complex problems into simpler pieces
  • Improving clarity of the code
  • Reuse of code
  • Information hiding

Functions in Python are first-class citizens. It means that functions have equal status with other objects in Python. Functions can be assigned to variables, stored in collections, or passed as arguments. This brings additional flexibility to the language.

Python function types

There are two basic types of functions: built-in functions and user defined functions. The built-in functions are part of the Python language; for instance dir, len, or abs. The user defined functions are functions created with the def keyword.

Python creating functions

A function is created with the def keyword. The statements in the block of the function must be indented.

def function():
    pass

The def keyword is followed by the function name with round brackets and a colon. The indented statements form the body of the function. The pass statement does nothing; it is used where a statement is required syntactically.

The function is later executed when needed. We say that we call the function. If we call a function, the statements inside the function body are executed. They are not executed until the function is called.

myfunc()

To call a function, we specify the function name with the round brackets.

Calling functions

The following example defines and calls two functions.

ret.py
"""
The ret.py script shows how to work with
functions in Python.
"""


def show_module_doc():
    print(__doc__)


def get_module_file():
    return __file__


a = show_module_doc()
b = get_module_file()

print(a, b)

The string at the top of the script is called the documentation string. It documents the current script. The file in which we put Python code is called a module.

We define two functions. The first function prints the module documentation string. The second returns the path of the module. A function may or may not return a value. If a function does not return a value, it implicitly returns None. The __doc__ and __file__ are special module attributes. Note that there are two underscores on both sides of the attribute.

$ python ret.py

The ret.py script shows how to work with
functions in Python.

None /home/user/python/ret.py

Definition before use

A function must be defined before it is called. A def statement is executed like any other statement; it creates the function object and binds it to the name.

func_prec.py
def f1():
    print('f1()')


f1()
f2()


def f2():
    print('f2()')

The f1 function is defined before it is called. The f2 function is called before its def statement was executed, so the interpreter raises a NameError.

$ python func_prec.py
f1()
Traceback (most recent call last):
  File "/home/user/python/func_prec.py", line 6, in <module>
    f2()
    ^^
NameError: name 'f2' is not defined. Did you mean: 'f1'?

Where to define functions

Functions can be defined inside modules, classes, or other functions. Functions defined inside a class are called methods.

defining.py
class Info:

    def say(self):
        print('This is Info class')


class Some:

    @staticmethod
    def f():
        print('f() static method')


def f():
    print('f() plain function')


def g():

    def f():
        print('f() inner function')

    f()


i = Info()
i.say()

Some.f()
f()
g()

In this example we define an f function in different places. The say method is called on an instance of the Info class. The static method, defined with the @staticmethod decorator, is called on the Some class name. The module-level f is a plain function. Inside g, there is an inner f function, which is visible only within g.

$ python defining.py
This is Info class
f() static method
f() plain function
f() inner function

Python functions are objects

Functions in Python are objects. They can be manipulated like other objects in Python.

fun_obj.py
def f():
    """This function prints a message"""
    print('Today it is a cloudy day')


print(isinstance(f, object))
print(type(f))
print(id(f))
print(f.__doc__)
print(f.__name__)

g = f
g()
print(g.__name__)

The isinstance function checks that f is an instance of object, the base of all Python objects. Like every object, the function has a type and a unique id, and it has attributes; we print the __doc__ and __name__ attributes. When we assign the function to the g variable, both names refer to the same function object.

$ python fun_obj.py
True
<class 'function'>
130789884034240
This function prints a message
f
Today it is a cloudy day
f

Functions in collections

Functions can be stored in collections and passed to other functions.

fun_coll.py
def add(x, y):
    return x + y


def sub(x, y):
    return x - y


def mul(x, y):
    return x * y


ops = {'+': add, '-': sub, '*': mul}

for sign, fn in ops.items():
    print(f'7 {sign} 3 = {fn(7, 3)}')


def apply(fn, vals):
    return [fn(v, v) for v in vals]


print(apply(add, [1, 2, 3]))
print(apply(mul, [1, 2, 3]))

We store three functions in a dictionary and call them by the operator sign. The apply function takes another function as its first parameter and calls it for each value. A function that takes or returns another function is called a higher-order function.

$ python fun_coll.py
7 + 3 = 10
7 - 3 = 4
7 * 3 = 21
[2, 4, 6]
[1, 4, 9]

Three kinds of functions in Python

Looking from a particular point of view, we can discern three kinds of functions: built-in functions that are always available, functions contained within modules, which must be imported, and functions defined by a programmer with the def keyword.

three_kinds.py
from math import sqrt


def cube(x):
    return x * x * x


print(abs(-1))
print(cube(9))
print(sqrt(81))

The abs function is a built-in function, readily accessible. The sqrt function is imported from the math module. The cube function is a custom defined function.

$ python three_kinds.py
1
729
9.0

Python return keyword

A function is created to do a specific task. Often there is a result from such a task. The return keyword is used to return values from a function. If a function does not have a return statement, it returns None.

returning.py
def show_message(msg):
    print(msg)


def cube(x):
    return x * x * x


x = cube(3)
print(x)

show_message('Computation finished.')
print(show_message('Ready.'))

The show_message function does not return a value explicitly; it prints a message to the console. The cube function computes an expression and returns its result, which we assign to the x variable. The last line produces two lines: the message printed by show_message and the None value implicitly returned by it.

$ python returning.py
27
Computation finished.
Ready.
None

Returning multiple values

A function can return more than one value. The objects after the return keyword are separated by commas.

returning2.py
def stats(vals):

    mx = max(vals)
    mn = min(vals)
    total = sum(vals)

    return mx, mn, len(vals), total


n = [1, 2, 3, 4, 5]

print(stats(n))

mx, mn, size, total = stats(n)
print(mx, mn, size, total)

The stats function returns four values. In fact, it returns a tuple containing these four values. The tuple can be unpacked into separate variables.

$ python returning2.py
(5, 1, 5, 15)
5 1 5 15

Python function redefinition

Python is dynamic in nature. It is possible to redefine an already defined function.

redefinition.py
from datetime import datetime


def show_message(msg):
    print(msg)


show_message('Ready.')


def show_message(msg):
    print(f'{datetime.now():%H:%M:%S} {msg}')


show_message('Processing.')

We define a show_message function and call it. Later we provide a new definition of the same function, which precedes the message with the current time. The second def statement rebinds the show_message name to a new function object.

$ python redefinition.py
Ready.
21:21:43 Processing.

Python function arguments

Most functions accept arguments. Arguments are values that are sent to the function. The names in the function definition are called parameters. The functions process the values and optionally return some value back.

fahrenheit.py
def c2f(c):
    return c * 9 / 5 + 32


print(c2f(100))
print(c2f(0))
print(c2f(30))
print(c2f(-40))

In our example, we convert Celsius temperature to Fahrenheit. The c2f function accepts one argument c, which is the Celsius temperature. The / operator always produces a float, so the results are floats.

$ python fahrenheit.py
212.0
32.0
86.0
-40.0

Default parameter values

The parameters of Python functions may have default values. A default value is used if no argument is provided.

default_args.py
def power(x, y=2):
    r = 1
    for _ in range(y):
        r = r * x
    return r


print(power(3))
print(power(3, 3))
print(power(5, 5))

Here we created a power function. The y parameter has a default value of 2. We can call the function with one or two arguments. Parameters with default values must follow parameters without them.

$ python default_args.py
9
27
3125

Mutable default values

Default values are evaluated only once, when the def statement is executed. A mutable default value, such as a list, is therefore shared by all calls of the function.

mutable_default.py
def add_item(item, items=[]):
    items.append(item)
    return items


print(add_item('sky'))
print(add_item('owl'))


def add_item2(item, items=None):
    if items is None:
        items = []
    items.append(item)
    return items


print(add_item2('sky'))
print(add_item2('owl'))

The add_item function keeps adding to the same list object, so the second call returns both items. The usual fix is to use None as the default and create a new list inside the function, as in add_item2.

$ python mutable_default.py
['sky']
['sky', 'owl']
['sky']
['owl']

Keyword arguments

Python functions can be called with keyword arguments. This means that when calling a function, we specify both the parameter name and a value. When arguments are passed without keywords, the order in which we pass them is crucial. With keywords, the order does not matter.

keyword_args.py
def display(name, age, sex):
    print(f'Name: {name}, Age: {age}, Sex: {sex}')


display('Lary', 43, 'M')
display(age=24, name='Joan', sex='F')
display('Paul', sex='M', age=31)

The first call uses positional arguments. The second call uses keyword arguments in a different order than the parameters. The third call mixes both: a positional argument may be followed by keyword arguments.

$ python keyword_args.py
Name: Lary, Age: 43, Sex: M
Name: Joan, Age: 24, Sex: F
Name: Paul, Age: 31, Sex: M

Positional argument after keyword argument

A positional argument cannot follow a keyword argument.

keyword_args2.py
def display(name, age, sex):
    print(f'Name: {name}, Age: {age}, Sex: {sex}')


display(age=24, name='Joan', 'F')

This is a syntax error; the program is not even started.

$ python keyword_args2.py
  File "/home/user/python/keyword_args2.py", line 5
    display(age=24, name='Joan', 'F')
                                    ^
SyntaxError: positional argument follows keyword argument

Positional-only and keyword-only parameters

The / and * markers in a parameter list restrict how arguments can be passed. Parameters before / are positional-only, parameters after * are keyword-only.

special_params.py
def describe(name, /, age, *, city):
    print(f'{name} is {age} years old and lives in {city}')


describe('Lucia', 23, city='Bratislava')
describe('Peter', age=41, city='Košice')

try:
    describe(name='Jana', age=30, city='Nitra')
except TypeError as e:
    print(e)

try:
    describe('Martin', 52, 'Žilina')
except TypeError as e:
    print(e)

The name parameter can be passed only by position, the age parameter either way, and the city parameter only by keyword. Positional-only parameters let the author rename them later without breaking callers; keyword-only parameters make calls with many options readable.

$ python special_params.py
Lucia is 23 years old and lives in Bratislava
Peter is 41 years old and lives in Košice
describe() got some positional-only arguments passed as keyword arguments: 'name'
describe() takes 2 positional arguments but 3 were given

Arbitrary number of positional arguments

Functions in Python can accept an arbitrary number of arguments.

arbitrary_args.py
def do_sum(*args):
    """Return the sum of all values"""

    print(type(args), args)

    r = 0
    for i in args:
        r += i

    return r


print(do_sum.__doc__)
print(do_sum(1, 2, 3))
print(do_sum(1, 2, 3, 4, 5))
print(do_sum())

We use the * operator to indicate that the function accepts an arbitrary number of positional arguments. They are collected into a tuple. The do_sum function returns the sum of all arguments. The first string in the function body is the function's documentation string.

$ python arbitrary_args.py
Return the sum of all values
<class 'tuple'> (1, 2, 3)
6
<class 'tuple'> (1, 2, 3, 4, 5)
15
<class 'tuple'> ()
0

Arbitrary number of keyword arguments

With the ** operator, a function accepts an arbitrary number of keyword arguments. They are collected into a dictionary.

details.py
def display(**details):

    print(type(details))

    for key, value in details.items():
        print(f'{key}: {value}')


display(name='Larry', age=43, sex='M')

We can provide an arbitrary number of key-value arguments. The function handles them all. Dictionaries keep the insertion order, so the arguments are printed in the order in which they were passed.

$ python details.py
<class 'dict'>
name: Larry
age: 43
sex: M

Combining parameter kinds

The different kinds of parameters can be combined in one function. The order is: positional parameters, *args, keyword-only parameters, and **kwargs.

all_kinds.py
def log(level, *args, sep=' ', **kwargs):
    print(level, sep.join(map(str, args)), kwargs)


log('INFO', 'server started')
log('WARN', 'disk', 'almost', 'full', sep='_', used='93%')

The log function has one regular parameter, collects the remaining positional arguments in args, has a keyword-only sep parameter with a default value, and collects other keyword arguments in kwargs. This is the same scheme that the built-in print function uses.

$ python all_kinds.py
INFO server started {}
WARN disk_almost_full {'used': '93%'}

Unpacking function return values

Unpacking is cutting an object, such as a list, into its elements. It is also called destructuring. The * operator collects all the remaining elements into a list. By convention, the _ name is used for values we want to ignore.

unpacking.py
def fn():
    return [1, 2, 3, 4, 5, 6]


a, b, c, d, e, f = fn()
print(a, b, c, d, e, f)

a, *mid, b = fn()
print(a, mid, b)

a, b, c, *_ = fn()
print(a, b, c)

a, b, c, *d = fn()
print(a, b, c, d)

*a, b, c, d = fn()
print(a, b, c, d)

We have a function that returns a list of values. First, all six values are unpacked into six variables. The starred mid variable takes all values but the first and the last one. With *_, we ignore the last three values. The starred variable can be at any position; *d takes the remaining values and *a takes the first three values.

$ python unpacking.py
1 2 3 4 5 6
1 [2, 3, 4, 5] 6
1 2 3
1 2 3 [4, 5, 6]
[1, 2, 3] 4 5 6

Unpacking function arguments

We can unpack values into function arguments. The * operator unpacks an iterable into positional arguments and the ** operator unpacks a dictionary into keyword arguments.

unpacking2.py
def fn(a, b, c, d, e, f):
    print(a, b, c, d, e, f)


def fn2(a, b, c, *d):
    print(a, b, c, d)


def fn3(a, b, c, *d, e, f):
    print(a, b, c, d, e, f)


def user(name, occupation):
    print(f'{name} is a {occupation}')


vals = [1, 2, 3, 4, 5, 6]

fn(*vals)
fn2(*vals)
fn3(*vals, e=7, f=8)

data = {'name': 'John Doe', 'occupation': 'gardener'}
user(**data)

We unpack a list into the arguments of three functions. Since the d parameter of fn3 collects the remaining positional arguments, the e and f parameters must be passed as keyword arguments. Finally, the dictionary keys are matched with the parameter names of the user function.

$ python unpacking2.py
1 2 3 4 5 6
1 2 3 (4, 5, 6)
1 2 3 (4, 5, 6) 7 8
John Doe is a gardener

Passing arguments

Python passes arguments by assignment: the parameter becomes a new name for the object that was passed. No copy is made. This is sometimes called call by sharing.

passing_args.py
def modify(x):
    x.pop()
    x.insert(0, 0)
    print('Inside modify():', x)


def rebind(x):
    x = [10, 20, 30]
    print('Inside rebind():', x)


n = [1, 2, 3, 4, 5]
print('Original list:', n)

modify(n)
print('After modify():', n)

rebind(n)
print('After rebind():', n)

The modify function changes the list object it received, so the change is visible outside the function. The rebind function assigns a new list to its local x name; this does not affect the caller's n variable. Immutable objects, such as numbers or strings, cannot be changed in place, so a function can never modify them for the caller.

$ python passing_args.py
Original list: [1, 2, 3, 4, 5]
Inside modify(): [0, 1, 2, 3, 4]
After modify(): [0, 1, 2, 3, 4]
Inside rebind(): [10, 20, 30]
After rebind(): [0, 1, 2, 3, 4]

Python local variables

A variable assigned in a function body has a local scope. It is valid only within the body of the function.

local_variable.py
name = 'Jack'


def f():
    name = 'Robert'
    print('Within function', name)


def g():
    print('Within function', name)


print('Outside function', name)
f()
g()
print('Outside function', name)

The f function creates a local name variable, which hides the global one; the global variable is not changed. The g function does not assign to name, so it reads the global variable.

$ python local_variable.py
Outside function Jack
Within function Robert
Within function Jack
Outside function Jack

Python global variables

If we want to change a global variable in a function, we must use the global keyword.

global_variable.py
name = 'Jack'


def f():
    global name
    name = 'Robert'
    print('Within function', name)


print('Outside function', name)
f()
print('Outside function', name)

Using the global keyword, we reference the variable defined outside the body of the function. The variable is given a new value, which is visible after the function call.

$ python global_variable.py
Outside function Jack
Within function Robert
Outside function Robert

UnboundLocalError

Python decides at compile time whether a name inside a function is local. A name that is assigned anywhere in the function body is local in the whole body.

unbound_local.py
counter = 0


def increment():
    counter += 1


try:
    increment()
except UnboundLocalError as e:
    print(e)

The counter += 1 statement assigns to counter, so counter is a local variable. Reading it before the assignment raises an UnboundLocalError. A global counter statement would fix it.

$ python unbound_local.py
cannot access local variable 'counter' where it is not associated with a value

Closures and nonlocal

An inner function can use the variables of its enclosing function, even after the enclosing function has returned. Such a function is called a closure. To assign to a variable of the enclosing function, we use the nonlocal keyword.

closure.py
def make_counter():

    count = 0

    def counter():
        nonlocal count
        count += 1
        return count

    return counter


c1 = make_counter()
c2 = make_counter()

print(c1(), c1(), c1())
print(c2())

The make_counter function returns the inner counter function. Each returned function remembers its own count variable, so the two counters are independent. There is a Python closures tutorial on ZetCode.

$ python closure.py
1 2 3
1

Recursive functions

A function can call itself. Such a function is called recursive. A recursive function must have a base case that stops the recursion.

recursion.py
import sys


def factorial(n):
    if n <= 1:
        return 1
    return n * factorial(n - 1)


print(factorial(5))
print(factorial(20))
print(sys.getrecursionlimit())

The factorial function computes the factorial of a number; the base case is n <= 1. Python integers have arbitrary precision, so large results are exact. The recursion depth is limited; the default limit is 1000 frames, and exceeding it raises a RecursionError.

$ python recursion.py
120
2432902008176640000
1000

Function annotations

Parameters and return values can be annotated with type hints. The annotations are not enforced by the interpreter; they are used by type checkers, editors, and documentation tools.

annotations.py
import inspect


def area(width: float, height: float = 1.0) -> float:
    return width * height


def make_user(name: str) -> User:
    return User(name)


class User:

    def __init__(self, name: str):
        self.name = name


print(area(3, 4.5))
print(area('ab', 3))

print(area.__annotations__)
print(inspect.signature(area))
print(make_user.__annotations__)

The area function accepts strings as well, because Python does not check the annotations. They are stored in the __annotations__ attribute and are shown by inspect.signature. Since Python 3.14, annotations are evaluated lazily, when they are first accessed. Therefore make_user can refer to the User class, which is defined later in the module. In Python 3.13 and earlier, the annotation is evaluated when the function is defined, which raises a NameError.

$ python annotations.py
13.5
ababab
{'width': <class 'float'>, 'height': <class 'float'>, 'return': <class 'float'>}
(width: float, height: float = 1.0) -> float
{'name': <class 'str'>, 'return': <class '__main__.User'>}
$ python3.13 annotations.py
Traceback (most recent call last):
  File "/home/user/python/annotations.py", line 8, in <module>
    def make_user(name: str) -> User:
                                ^^^^
NameError: name 'User' is not defined

Python anonymous functions

It is possible to create anonymous functions in Python. Anonymous functions do not have a name. With the lambda keyword, little anonymous functions can be created. Anonymous functions are also called lambda functions. They are restricted to a single expression. There is a Python lambda functions tutorial on ZetCode.

lambda_fun.py
y = 6

z = lambda x: x * y
print(z(8))

cs = [-10, 0, 15, 30, 40]

ft = map(lambda t: 9 / 5 * t + 32, cs)
print(list(ft))

words = ['sky', 'Falcon', 'owl', 'Arrow', 'cup']
print(sorted(words, key=lambda w: w.lower()))

The x is a parameter of the lambda function. It is followed by a colon and the expression that is evaluated when the function is called. The lambda is assigned to the z variable; when called with 8, it returns 48. The map function applies an anonymous function to each element of a list of Celsius temperatures. The sorted function uses a lambda as the key, so the words are sorted case-insensitively.

$ python lambda_fun.py
48
[14.0, 32.0, 59.0, 86.0, 104.0]
['Arrow', 'cup', 'Falcon', 'owl', 'sky']

Source

Defining functions - Python tutorial

In this article we covered Python functions.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all Python tutorials.

You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.