Table of Contents

Python: functions vs methods

Welcome back to Cameron’s Corner! Today, we’re tackling a common but sometimes confusing distinction in Python: functions vs methods. At first glance, they might seem interchangeable—both let you define reusable blocks of code—but there’s an important difference in how they interact with objects. And when we start looking at bound vs unbound methods, things get even more interesting. Let’s break it down!

In Python, the difference between a function and a method often comes down to where it's defined and how it's called. But beyond that, there’s a subtle but important distinction between bound and unbound methods, especially when working with classes.

Let's take a look at a basic example:

def func(obj):
    return obj.value + 1

class T():
    def __init__(self):
        self.value = 3
    
    def func(self):
        return self.value * 2
    
t = T()

print(
    f'{func   = }', # standalone func
    f'{t.func = }', # bound func (it is bound to the instance of `t`)
    f'{T.func = }', # unbound T.func
    sep='\n',
)
func   = <function func at 0x700b06cd27a0>
t.func = <bound method T.func of <__main__.T object at 0x700b1fa0f800>>
T.func = <function T.func at 0x700b06cd28e0>

Here’s what’s happening:

  • func is just a regular, standalone function. It takes any object and expects it to have a .value attribute.

  • Inside the class T, we define a method func. This is an instance method, meaning it automatically receives self as its first argument when called on an instance.

  • When we access t.func, Python gives us a bound method—this method is now “bound” to the specific instance t. When we later call t.func(), Python automatically passes t as the self argument.

  • T.func on the other hand is an unbound method. Accessing the method from the class itself doesn't tie it to any instance, so you have to pass an instance explicitly when calling it.

Printing these objects makes the distinction clear. You can see whether Python treats them as just raw functions or as bound methods with instance context attached.

Using the above points, we can call ANY of these functions as follows:

print(
    f'{func(t)   = }',
    f'{t.func()  = }',
    f'{T.func(t) = }',
    sep='\n',
)
func(t)   = 4
t.func()  = 6
T.func(t) = 6

Bound or Unbound, who cares?

This difference becomes important when you start working with inheritance and overriding methods. Consider this extension:

class TChild(T):
    def func(self):
        return self.value ** 2

tc = TChild()
tc.func()
9

Here, TChild inherits from T but overrides the func method. When we create an instance tc and call tc.func(), Python resolves the method to TChild.func and runs that version, giving us 3 ** 2 == 9. This is standard method overriding.

But here's the interesting part: what if we deliberately bypass the method resolution order (MRO) and call the parent class's version of func directly?

T.func(tc)
6

This works! Even though tc is an instance of TChild, we're directly calling T.func, passing in tc manually as the self argument. Remember, T.func is unbound when accessed this way, so it doesn't automatically get a self. We have to give it an instance ourselves.

Why does this matter?

  • Bound methods simplify life by automatically handling the first argument (self), ensuring it’s always the instance you're working with.

  • Unbound methods (or class functions) can be called manually with any compatible object, regardless of its actual type. This is part of Python’s flexibility, but it can make things tricky if you're not careful.

In practice:

  • You usually want to call methods through instances (obj.method()), relying on Python's method resolution in order to do the right thing.

  • But understanding how methods bind—and when they don't—is crucial when working with advanced patterns like delegation, mixins, or direct super-class method calls.

When to write a Function vs Method

In Python, the key difference between a function and a method is whether it's tied to an object.

  • Function: Defined outside of a class and operates on passed arguments.

  • Method: Defined inside a class and operates on an instance (self) or the class itself (cls).

Use a Function When:

  • The logic doesn’t rely on object state.

  • You’re writing a general-purpose operation.

def add(a, b): # does not rely on stored state
    return a + b

Use a Method When:

  • The logic depends on or modifies object state.

  • You want behavior tied to instances of a class.

class Counter:
    def __init__(self):
        self.count = 0

    def increment(self): # updates the state of the Counter.count
        self.count += 1
        return self.count

Of course the above is simply some guidance as there are always exceptions to the "rule" and not to mention, not to mention there is no mechanism in Python that prevents us from writing a function that manipulates object state or only works with a single specific type.

The idea here is to write code that will be the least surprising to someone working with it, and if we adhere to these principles we are going to write predictable code.

Feature

Function

Method

Defined in

Module/Global

Inside a class

First parameter

Explicit arguments

self or cls

Called on

Directly

Instance or class

Tied to state?

No

Yes (usually)

Use functions for independent logic and methods when behavior belongs to objects.

Wrap-Up

There we have it! A brief comparison of functions vs methods in Python. To wrap up: functions and methods share similarities, but the key distinction is that methods belong to objects and typically operate on their internal state, while functions are independent and work purely with the arguments you pass them. When your logic depends on an object's data or behavior, use a method; when it doesn’t, a standalone function is often the clearer choice. Talk to you all again next week!

How do you use functions and methods in your code? Let me know on the DUTC Discord server.

Table of Contents
Table of Contents