Python · object-oriented

Methods

Instance, class and static methods: what each one receives, and how a call is turned into a function call.

Instance methods


          # instance method
          class Calculator:
              def add(self, a, b):
                  return a + b
          calc = Calculator()
          calc.add(5, 3)
          # ans: 8
        
calc.add(5, 3)
what you write
Calculator.add(calc, 5, 3)
what Python runs: the object is passed in as self
5 + 3 → 8
inside: a = 5, b = 3

Class methods


          # class method
          class Dog:
              count = 0
              def __init__(self):
                  Dog.count += 1
              @classmethod
              def get_count(cls):
                  return cls.count
          Dog().get_count()
          # ans: 1
        
Dog().get_count()
first Dog() makes a dog...
Dog.count: 0 → 1
...and __init__ bumps the shared counter
Dog.get_count(Dog)
a class method receives the class as cls, even when called on an instance
cls.count → 1
done

Static methods


          # static method
          class MathUtils:
              @staticmethod
              def add(a, b):
                  return a + b
          MathUtils.add(5, 3)
          # ans: 8 (no instance needed)
        
MathUtils.add(5, 3)
a static method is a plain function kept inside the class
add(5, 3) → 8
nothing extra is passed in: no self, no cls

Methods use self


          # self accesses the instance
          class Person:
              def __init__(self, name):
                  self.name = name
              def greet(self):
                  return f"Hi, I'm {self.name}"
          Person("Alice").greet()
          # ans: "Hi, I'm Alice"
        
Person("Alice").greet()
a new person whose name is "Alice"
Person.greet(<Person: name='Alice'>)
that object becomes self
f"Hi, I'm {self.name}" → "Hi, I'm Alice"
self.name reads the object's own attribute

Three kinds of method

KindFirst parameterUse it for
instance methodself: the objectreading and changing one object's data
@classmethodcls: the classclass-wide data, alternative constructors
@staticmethodnothinghelpers that belong with the class but need neither

Exercises

Predict each result, then press → to check.

Exercise 1


        # classmethod vs instance?
        class MyClass:
            @classmethod
            def method(cls):
                return cls
        MyClass.method()
        # ans: <class '__main__.MyClass'>
      

Exercise 2


        # staticmethod access?
        class MyClass:
            x = 5
            @staticmethod
            def method():
                return x
        MyClass.method()
        # ans: NameError (no self or cls to reach x through)
      

Class attributes aren't in scope inside methods; it would need MyClass.x.

Exercise 3


        # what does a classmethod receive?
        class MyClass:
            @classmethod
            def method(cls):
                return type(cls)
        MyClass.method()
        # ans: <class 'type'>
      

A class is itself an object, and its type is type.

Exercise 4


          # call an instance method on the class?
          class MyClass:
              def method(self):
                  pass
          MyClass.method()
          # ans: TypeError (missing self)
        
MyClass.method()
called on the class, not on an object
method(self=?)
no object, so nothing fills self: missing 1 required argument

Exercise 5


          # staticmethod with self?
          class MyClass:
              @staticmethod
              def method(self):
                  pass
          obj = MyClass()
          obj.method()
          # ans: TypeError (no implicit self)
        
obj.method()
called on an object...
method(self=?)
...but static methods are never given the object, so self is missing

Exercise 6


        # classmethod inheritance?
        class Base:
            @classmethod
            def who(cls):
                return cls.__name__
        class Derived(Base):
            pass
        Derived.who()
        # ans: "Derived"
      

cls is the class the method was called on, not the one it was written in.

Exercise 7


        # method chaining?
        class Builder:
            def set_x(self, x):
                self.x = x
                return self
            def set_y(self, y):
                self.y = y
                return self
        Builder().set_x(1).set_y(2)
        # ans: a Builder with x=1 and y=2 (each setter returns self)
      

Exercise 8


        # private method?
        class MyClass:
            def __private(self):
                pass
        obj = MyClass()
        obj.__private()
        # ans: AttributeError (name mangling)
      

Inside the class, __private is renamed _MyClass__private.

Exercise 9


        # method vs function?
        class MyClass:
            def method(self):
                pass
        type(MyClass.method)
        # ans: <class 'function'>
        type(MyClass().method)
        # ans: <class 'method'>
      

Looked up on an object, the function is bound: it remembers the object to pass as self.

Exercise 10


        # alternative constructor?
        class Point:
            def __init__(self, x, y):
                self.x = x
                self.y = y
            @classmethod
            def origin(cls):
                return cls(0, 0)
        Point.origin()
        # ans: a new Point with x=0, y=0
      

Remember

obj.m(a)

is Class.m(obj, a): the object becomes self.

@classmethod

Receives the class as cls; great for alternative constructors.

@staticmethod

Receives nothing extra.

__name

Double underscores trigger name mangling.