Fundamentals 8 min read

What Really Happens Inside Python When You Call a Function?

This article explains step by step how Python creates a function object, builds a call stack, handles parameters, executes the body, performs garbage collection, and manages recursion, illustrating each stage with clear code examples and diagrams.

Code Mala Tang
Code Mala Tang
Code Mala Tang
What Really Happens Inside Python When You Call a Function?

Today we explore an interesting topic: what actually happens inside Python when a function is called.

Consider a very simple function:

<code>def greet(name):
    return f"Hello, {name}!"

print(greet("Alice"))</code>

When we define and call this function, Python performs many internal operations to produce the expected result.

Step 1: Create a Function Object

In Python, defining a function creates a function object that is stored in memory like any other variable.

<code>def greet(name):
    return f"Hello, {name}!"
print(type(greet))</code>

The object greet has a name and a reference in memory. You can inspect it with:

<code>print(greet.__name__)
print(greet)</code>

Step 2: Create an Execution Stack

When greet("Alice") is called, Python creates a new stack frame on top of the call stack.

The main stack frame is where the program runs. Each function call pushes a new frame; when the function returns, the frame is popped and execution resumes in the previous frame.

Example:

<code>def bar():
    print("Inside bar()")

def foo():
    print("Inside foo(), calling bar()")
    bar()
    print("Returned to foo() after bar() finished")

def main():
    print("Inside main(), calling foo()")
    foo()
    print("Returned to main() after foo() finished")
    print("Inside main(), calling bar()")
    bar()
    print("Returned to main() after bar() finished")

main()</code>

The following diagram visualizes the stack changes:

Program starts with main() .

main() calls foo() , creating a stack frame for foo() .

Inside foo() , bar() is called, adding a frame for bar() .

When bar() finishes, its frame is removed and control returns to foo() .

After foo() finishes, its frame is removed and execution returns to main() .

main() calls bar() again, adding another frame.

When this bar() finishes, only the main() frame remains.

Step 3: Parameter Handling

When arguments are passed, Python either passes a reference to the object (for mutable types like lists or dictionaries) or passes a copy of the reference (for immutable types like integers and strings).

Mutable example:

<code>def modify_list(lst):
    lst.append(100)

my_list = [1, 2, 3]
modify_list(my_list)
print(my_list)</code>

Immutable example:

<code>def modify_number(num):
    num += 10

x = 5
modify_number(x)
print(x)</code>

Because integers are immutable, num += 10 creates a new integer object rather than modifying the original.

Step 4: Execute the Function Body

During execution, parameters are assigned to local variables and the function body runs line by line until a return statement provides the result.

Example:

<code>def calculate(a, b):
    result = a + b
    return result

x = calculate(3, 7)</code>

Here, a receives 3, b receives 7, the sum is computed, and 10 is returned and stored in x .

Step 5: Garbage Collection

After a function finishes, its local variables are cleared from memory by Python’s automatic garbage collector.

<code>def example():
    x = 100

example()
print(x)  # NameError because x is not defined outside the function</code>

If an object is still referenced (e.g., returned from the function), it will not be collected.

<code>def outer():
    x = [1, 2, 3]
    return x

y = outer()
print(y)</code>

Recursion

Recursive functions call themselves until a base condition is met, adding a new stack frame for each call. Excessive recursion can cause a stack overflow.

<code>def countdown(n):
    if n == 0:
        print("Done")
        return
    print(n)
    countdown(n - 1)

countdown(5)</code>

An infinite recursion example:

<code>def infinite():
    return infinite()

infinite()  # RecursionError: maximum recursion depth exceeded</code>

You can check Python’s default recursion limit (usually 1000) with:

<code>import sys
print(sys.getrecursionlimit())</code>
PythonGarbage Collectionrecursionstack frameparameter passingFunction Call
Code Mala Tang
Written by

Code Mala Tang

Read source code together, write articles together, and enjoy spicy hot pot together.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.